Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding section index headers for A to Z UITableView scrolling

I am trying to add some section headers for a contacts table view. I have a list of contacts in an Array which might be like this:

Bill Apple
Borat
Steve Test

I have added the following:

func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    let currentCollation = UILocalizedIndexedCollation.currentCollation() as UILocalizedIndexedCollation
    let sectionTitles = currentCollation.sectionTitles as NSArray
    return sectionTitles.objectAtIndex(section) as? String
}

func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? {
    let currentCollation = UILocalizedIndexedCollation.currentCollation() as UILocalizedIndexedCollation
    return currentCollation.sectionIndexTitles as [String]
}

func tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
    let currentCollation = UILocalizedIndexedCollation.currentCollation() as UILocalizedIndexedCollation
    return currentCollation.sectionForSectionIndexTitleAtIndex(index)
}

This gets the A-Z headings to show up. However as there is only one section and one array holding the contacts the actual functionality does not work.

Should we be sorting the Array into an alphabetised Array in some sort? So that it has multiple sections? Or is there an easier way to approach this?

like image 882
StuartM Avatar asked Nov 15 '25 11:11

StuartM


2 Answers

Should we be sorting the Array into an alphabetised Array in some sort? So that it has multiple sections?

Yes.

You added the delegate methods for the section index, but you didn't show any code that actually performed the collation.

Here's some sample code from NSHipster showing how to use UILocalizedIndexedCollation to collate an array of objects (e.g., contacts) into an array of sections of objects (e.g., contacts grouped by section):

let collation = UILocalizedIndexedCollation.currentCollation()
var sections: [[AnyObject]] = []
var objects: [AnyObject] = [] {
    didSet {
        let selector: Selector = "localizedTitle"
        sections = Array(count: collation.sectionTitles.count, repeatedValue: [])

        let sortedObjects = collation.sortedArrayFromArray(objects, collationStringSelector: selector)
        for object in sortedObjects {
            let sectionNumber = collation.sectionForObject(object, collationStringSelector: selector)
            sections[sectionNumber].append(object)
        }

        self.tableView.reloadData()
    }
}

You'll also want to update your remaining tableView delegates to use the sections array:

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return sections.count
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return sections[section].count
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let contact = sections[indexPath.section][indexPath.row]
    ...
} 

FYI, you can simplify your delegate code by assigning the collation property to your view controller, instead of repeatedly declaring a local variable for the (current) collation inside each delegate method. For example:

override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String {
    return collation.sectionTitles[section]
}

The tableview is very easy to handle, if you prepare your data in a structure organized like your display.

As you know, you do not receive the section header in your delegate, but the IndexPath. So, it is most convenient to restructure your data to be accessible by IndexPath.

  • Keep your data list in a backing store, that is not directly used for the table view. This is either on the server for online applications or in managed data for offline applications or held in memory if the data is not too big. The backing store is optimized for searching and filtering.
  • create a NSArray<DataNode*> for the sections. Order the array as your table will be ordered.
  • in each DataNode, you have meta-data, like the title of the section, collapsed information etc. and real data, like the section header node (if there is such a thing) and the children of the section as the NSArray<DataNode*> of addresses. Again, ordered in the way you want them in display.

All delegate functions are now a pice of cake and more or less reduce to

tableData.count  // count of sections
tableData[indexPath.section] // section header data
tableData[indexPath.section].children.count // rows in section
tableData[indexPath.section].children[indexPath.row]  // row data

If you have a "live-filter" function, where the user types a few chars and you want to reduce the list to the matches, you actually rebuild the DataNode lists from your original data list.

-(void) buildSearchresult:(NSString *)criteria {
    NSArray *result = [backingListService findWithCriteria:criteria];
    NSArray *nodes = [displayListService structureResult:result withOption:OPTAlphaSort];
    [[NotificationCenter defaultCenter] postNotification:@"listUpdate" object:nodes];
}

Run the search and restructuring of the data in the background and send the new list with NotificationCenter to the delegate.

The delegate will catch the updated list and put it in place for the [self.tableView reloadData]:

-(void)onUpdateListData:(NSNotification *)mesg {
    NSArray *nodes = mesg.object;

    self.displayData = nodes;
    [self.tableView reloadData];
}  

This is how I do it :-)

like image 21
thst Avatar answered Nov 18 '25 19:11

thst



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!