在 SwiftUI 中动态创建列表部分

Dynamically create section for list in SwiftUI

我正在尝试在 SwiftUI 中使用 header 为 List 动态创建部分。

这是我的数组:

 var lists = [a list of names with A to Z] // array of strings

然后我尝试获取第一个字母:

var firstCharacters: [Character] {
        var firstCharacters = [Character]()
        for list in lists.sorted(by: {[=11=] < }) {
            if let character = list.first, !firstCharacters.contains(character) {
                firstCharacters.append(character)
            }
        }
        return firstCharacters
    }

我这样创建了 list

List {
    ForEach(firstCharacters, id: \.self) { charachter in
        Section(header: Text("\(charachter.description)")) {
        ForEach(Array(lists.enumerated()), id:\.element) {  index, element in
            Text("Name \(element), Id: \(index)")

                })
            }
        }
    }
}

现在我在向列表中添加部分时遇到问题,现在确定如何与列表合并。

以下将打印每个部分中的名称:

struct ContactsView: View {
    let names = ["James", "Steve", "Anna", "Baxter", "Greg", "Zendy", "Astro", "Jenny"]

    var firstChar: [Character] {
        let array =  names
            .compactMap({ [=10=].first })
                    
        // Set is just a quick way to remove duplicates.
        return Array(Set(array))
            .sorted(by: { [=10=] <  })
    }

    var body: some View {
        List {
            ForEach(firstChar, id: \.self) { char in
                Section(header: Text("\(char.description)")) {
                    ForEach(names, id: \.self) { name in
                        if name.first == char {
                            Text(name)
                        }
                    }
                }
            }
        }
    }
}

尽管更好的方法可能是不使用 2 个不同的数组,而是将它们合并成一个,其中第一个字母是键,值是名称。

这样您就不必遍历每个部分的每个名称。

一个更好的分割方法是把你的单词列表变成一个以第一个字母为关键字的字典,如下所示:

var wordDict: [String:[String]] {
    let letters = Set(lists.compactMap( { [=10=].first } ))
    var dict: [String:[String]] = [:]
    for letter in letters {
        dict[String(letter)] = lists.filter( { [=10=].first == letter } ).sorted()
    }
    return dict
}

然后像这样在 List 中使用 Dict

    List {
        // You then make an array of keys, sorted by lowest to highest
        ForEach(Array(wordDict.keys).sorted(by: <), id: \.self) { character in
            Section(header: Text("\(character)")) {
                // Because a dictionary lookup can return nil, we need to provide a response
                // if it fails. I used [""], though I could have just force unwrapped.
                ForEach(wordDict[character] ?? [""], id: \.self) { word in
                    Text("Name \(word)")
                }
            }
        }
    }

这样您就不必为每个字母遍历整个列表。它在创建 Dict 时完成一次。您不再需要 var firstChar.

我的推荐是一个视图模型,它将一个字符串数组分组到一个 Section 结构中 Dictionary(grouping:by:)

class ViewModel : ObservableObject {
    struct Section: Identifiable {
        let letter : String
        let names : [String]
    
        var id : String { letter }
    }
    
    @Published var sections = [Section]()
    
    var names = [String]() {
        didSet {
            let grouped = Dictionary(grouping: names, by: {[=10=].prefix(1)})
            sections = grouped.keys.sorted().map{Section(letter: String([=10=]), names: grouped[[=10=]]!)}
        }
    }
}

只要数组的内容被修改,部分数组(和视图)就会更新。


onAppear 的视图中传递 一些 名称

struct ContentView: View {
    @StateObject private var model = ViewModel()

    var body: some View {
        
        List(model.sections) { section in
            Section(section.letter) {
                ForEach(section.names, id: \.self, content: Text.init)
            }
        }
        .onAppear {
            model.names = ["Aaran", "Aaren", "Aarez", "Badsha", "Bailee", "Bailey", "Bailie", "Bailley", "Carlos", "Carrich", "Carrick", "Carson", "Carter", "Carwyn", "Dante", "Danyal", "Danyil", "Danys", "Daood", "Dara", "Darach", "Daragh", "Darcy", "D'arcy", "Dareh", "Eisa", "Eli", "Elias", "Elijah", "Eliot", "Elisau", "Finn", "Finnan", "Finnean", "Finnen", "Finnlay", "Geoff", "Geoffrey", "Geomer", "Geordan", "Hamad", "Hamid", "Hamish", "Hamza", "Hamzah", "Han", "Idris", "Iestyn", "Ieuan", "Igor", "Ihtisham", "Jarno", "Jarred", "Jarvi", "Jasey-Jay", "Jasim", "Jaskaran", "Jason", "Jasper", "Jaxon", "Kabeer", "Kabir", "Kacey", "Kacper", "Kade", "Kaden", "Kadin", "Kadyn", "Kaeden", "Kael", "Kaelan", "Kaelin", "Kaelum", "Kai", "Kaid", "Kaidan", "Kaiden", "Kaidinn", "Kaidyn", "Kaileb", "Kailin", "Karsyn", "Karthikeya", "Kasey", "Kash", "Kashif", "Kasim", "Kasper", "Kasra", "Kavin", "Kayam", "Leiten", "Leithen", "Leland", "Lenin", "Lennan", "Lennen", "Lennex", "Lennon", "Lennox", "Lenny", "Leno", "Lenon", "Lenyn", "Leo", "Leon", "Leonard", "Leonardas", "Leonardo", "Lepeng", "Leroy", "Leven", "Levi", "Levon", "Machlan", "Maciej", "Mack", "Mackenzie", "Mackenzy", "Mackie", "Macsen", "Macy", "Madaki", "Nickson", "Nicky", "Nico", "Nicodemus", "Nicol", "Nicolae", "Nicolas", "Nidhish", "Nihaal", "Nihal", "Nikash", "Olaoluwapolorimi", "Ole", "Olie", "Oliver", "Olivier", "Peter", "Phani", "Philip", "Philippos", "Phinehas", "Phoenix", "Phoevos", "Pierce", "Pierre-Antoine", "Pieter", "Pietro", "Piotr", "Porter", "Prabhjoit", "Prabodhan", "Praise", "Pranav", "Rasul", "Raul", "Raunaq", "Ravin", "Ray", "Rayaan", "Rayan", "Rayane", "Rayden", "Rayhan", "Santiago", "Santino", "Satveer", "Saul", "Saunders", "Savin", "Sayad", "Sayeed", "Sayf", "Scot", "Scott", "Scott-Alexander", "Seaan", "Seamas", "Seamus", "Sean", "Seane", "Sean-James", "Sean-Paul", "Sean-Ray", "Seb", "Sebastian", "Sebastien", "Selasi", "Seonaidh", "Sephiroth", "Sergei", "Sergio", "Seth", "Sethu", "Seumas", "Shaarvin", "Shadow", "Shae", "Shahmir", "Shai", "Shane", "Shannon", "Sharland", "Sharoz", "Shaughn", "Shaun", "Tadhg", "Taegan", "Taegen", "Tai", "Tait", "Uilleam", "Umair", "Umar", "Umer", "Umut", "Urban", "Uri", "Usman", "Uzair", "Uzayr", "Valen", "Valentin", "Valentino", "Valery", "Valo", "Vasyl", "Vedantsinh", "Veeran", "Victor", "Victory", "Vinay", "Vince", "Wen", "Wesley", "Wesley-Scott", "Wiktor", "Wilkie", "Will", "William", "William-John", "Willum", "Wilson", "Windsor", "Wojciech", "Woyenbrakemi", "Wyatt", "Wylie", "Wynn", "Xabier", "Xander", "Xavier", "Xiao", "Xida", "Xin", "Xue", "Yadgor", "Yago", "Yahya", "Yakup", "Yang", "Yanick", "Yann", "Yannick", "Yaseen", "Yasin", "Yasir", "Yassin", "Yoji", "Yong", "Yoolgeun", "Yorgos", "Youcef", "Yousif", "Youssef", "Yu", "Yuanyu", "Yuri", "Yusef", "Yusuf", "Yves", "Zaaine", "Zaak", "Zac", "Zach", "Zachariah", "Zacharias", "Ziyaan", "Zohaib", "Zohair", "Zoubaeir", "Zubair", "Zubayr", "Zuriel"]
        }
    }
}