GIJoeCodes
GIJoeCodes

Reputation: 1850

How to populate sections of an indexed UITableView with different array for sectionIndexTitles

I'm using an array of people's names to load a table. I want to separate the sections based on each person's state of residence while keeping the sectionIndexTitles limited to the first letter of the state's name. My sectionIndexTitles would be i.e. ["A", "C", "D", "F", "G", "H", "I", "K", "L", "M", "N", "O", "P", "R", "S", "T", "U", "V", "W"] while the table sections would be separated into all 50 states.

In essence, the letter A (index 0) would belong to Alaska, Alabama, Arkansas, and Arizona. There is no B so the next letter C (index 2) should scroll to California which has a section count of 4.

The problem I run into is that the sections are obviously not properly indexed therefore when tapping on any indexed title other than the letter A, the tableview does not scroll to the proper letter.

Upvotes: 0

Views: 160

Answers (1)

RajeshKumar R
RajeshKumar R

Reputation: 15748

Group the people array by the state name and create array of tuple from the dictionary. Then use that array in tableview data source methods

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    struct Person {
        var name: String
        var state: String
    }
    var allPeople = [Person]()
    var groupedPeople = [(state:String, people:[Person])]()
    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        allPeople = [Person(name: "a", state: "Alaska"), Person(name: "x", state: "Florida"),
                     Person(name: "c", state: "California")]
        groupedPeople = Dictionary(grouping: allPeople, by: { $0.state }).sorted(by: { $0.key < $1.key })
            .map({ (state:$0.key, people:$0.value)
            })
    }
    func sectionIndexTitles(for tableView: UITableView) -> [String]? {
        return Array(Set(groupedPeople.map({ String($0.state.first!) })))
    }
    func numberOfSections(in tableView: UITableView) -> Int {
        return groupedPeople.count
    }
    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return groupedPeople[section].state
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return groupedPeople[section].people.count
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell") ?? UITableViewCell(style: .subtitle, reuseIdentifier: "cell")
        cell.textLabel?.text = groupedPeople[indexPath.section].people[indexPath.row].name
        cell.detailTextLabel?.text = groupedPeople[indexPath.section].people[indexPath.row].state
        return cell
    }
}

Update

When you have more sections than sectionIndexTitles you should implement sectionForSectionIndexTitle method and return appropriate section index.

func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
    if let index = groupedPeople.firstIndex(where: { $0.state.hasPrefix(title) }) {
        return index
    }
    return 0
}

Upvotes: 2

Related Questions