Reputation: 1850
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
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