How to make alphabetically section headers in table view with a mutable data source

前端 未结 3 1305
一生所求
一生所求 2021-01-05 07:59

I store strings of a view controller in a string array. I import this string array as a Data Source in my table view. This all works smoothly. But now I would like to sort t

相关标签:
3条回答
  • 2021-01-05 08:27

    I would change the way you store your contacts to a dictonary with the initial letters as keys and put the names that correspond to that initial letter into a subarray:

    contacts = ["A": ["Anton", "Anna"], "C": ["Caesar"]]
    

    I simplified the way of the contacts here (in form of strings), but you get the concept.

    I would also save the section number of the letter in a seperate array like this:

    letters = ["A", "C"]
    

    Keep the array sorted and organized, so check after each insertion/deletion/update. This is not part of the table view implementation. I would make the Viewcontroller a delegate of the phonebook, so you can fire an update-like method from the phonebook to update the table.

    How to get the data for the data source:

    the number of sections:

    letters.count
    

    the section title for section at index i is

    letters[i]
    

    the number of cells in a section i is

    contacts[letters[i]].count
    

    and the content for a specific cell c in section i is:

    contacts[letters[i]][c]
    

    Feel free to ask further questions if anything is still not clear.

    UPDATE - How to generate the arrays:

    I don't require the data to be sorted, if you pass it already sorted, you can delete the sorting lines below ...

    let data = ["Anton", "Anna", "John", "Caesar"] // Example data, use your phonebook data here.
    
    // Build letters array:
    
    var letters: [Character]
    
    letters = data.map { (name) -> Character in
        return name[name.startIndex]
    }
    
    letters = letters.sort()
    
    letters = letters.reduce([], combine: { (list, name) -> [Character] in
        if !list.contains(name) {
            return list + [name]
        }
        return list
    })
    
    
    // Build contacts array:
    
    var contacts = [Character: [String]]()
    
    for entry in data {
    
        if contacts[entry[entry.startIndex]] == nil {
            contacts[entry[entry.startIndex]] = [String]()
        }
    
        contacts[entry[entry.startIndex]]!.append(entry)
    
    }
    
    for (letter, list) in contacts {
        list.sort()
    }
    

    For Swift 3:

    let data = ["Anton", "Anna", "John", "Caesar"] // Example data, use your phonebook data here.
    
    // Build letters array:
    
    var letters: [Character]
    
    letters = data.map { (name) -> Character in
        return name[name.startIndex]
    }
    
    letters = letters.sorted()
    
    letters = letters.reduce([], { (list, name) -> [Character] in
        if !list.contains(name) {
            return list + [name]
        }
        return list
    })
    
    
    // Build contacts array:
    
    var contacts = [Character: [String]]()
    
    for entry in data {
    
        if contacts[entry[entry.startIndex]] == nil {
            contacts[entry[entry.startIndex]] = [String]()
        }
    
        contacts[entry[entry.startIndex]]!.append(entry)
    
    }
    
    for (letter, list) in contacts {
        contacts[letter] = list.sorted()
    }
    

    I ran the code in playground and got the following outputs for

    letters:

    ["A", "C", "J"]
    

    contacts:

    ["J": ["John"], "C": ["Caesar"], "A": ["Anton", "Anna"]]
    
    0 讨论(0)
  • 2021-01-05 08:31

    I did it within one loop, not few (Swift 4):

    struct ContactData {
        let longName: String
        let phones: [String]
        let thumbnailImageData: Data?
    }
    var contacts = [ContactData]()
    var tableViewSource = [Character : [ContactData]]()
    var headerTitles = [Character]()
    
    func createContactsData(completionHandler: @escaping () -> Swift.Void) {
        contacts = extractContacts() // convert CNContact to custom ContactData
        tableViewSource.removeAll()
        var prevChar: Character?
        var currentBatch: [ContactData]!
        contacts.forEach { contact in
            guard let firstChar = contact.longName.first else {
                return
            }
            if prevChar != firstChar {
                if prevChar != nil {
                    tableViewSource[prevChar!] = currentBatch
                }
                prevChar = firstChar
                currentBatch = [ContactData]()
            }
            currentBatch.append(contact)
        }
    
        let allKeys = Array(tableViewSource.keys)
        let sortedSymbols = allKeys.sorted(by: {$0 < $1})
        headerTitles = sortedSymbols
        completionHandler()
    }
    
    0 讨论(0)
  • 2021-01-05 08:38

    For Swift 3. Thank you @Stefan! Here is my version with Set

    var tableViewSource: [Character : [String]]!
    var tableViewHeaders: [Character]!
    
    let data = ["Anton", "Anna", "John", "Caesar"]
    
    func createTableData(wordList: [String]) -> (firstSymbols: [Character], source: [Character : [String]]) {
    
        // Build Character Set
        var firstSymbols = Set<Character>()
    
        func getFirstSymbol(word: String) -> Character {
            return word[word.startIndex]
        }
    
        wordList.forEach {_ = firstSymbols.insert(getFirstSymbol(word: $0)) }
    
        // Build tableSourse array
        var tableViewSourse = [Character : [String]]()
    
        for symbol in firstSymbols {
    
            var words = [String]()
    
            for word in wordList {
                if symbol == getFirstSymbol(word: word) {
                    words.append(word)
                }
            }
    
            tableViewSourse[symbol] = words.sorted(by: {$0 < $1})
        }
    
        let sortedSymbols = firstSymbols.sorted(by: {$0 < $1})
    
        return (sortedSymbols, tableViewSourse)
    }
    
    func getTableData(words: [String]) {
        tableViewSource = createTableData(wordList: words).source
        tableViewHeaders = createTableData(wordList: words).firstSymbols
    }
    
    getTableData(words: data)
    
    print(tableViewSource)  // ["J": ["John"], "C": ["Caesar"], "A": ["Anna", "Anton"]]
    print(tableViewHeaders) // ["A", "C", "J"]
    
    0 讨论(0)
提交回复
热议问题