UISearchDisplayController and UILocalizedIndexedCollation

Thursday, December 31st, 2009

Over the years, I have been, for the most part, pleased with Apple’s documentation for Cocoa, and its documentation for Cocoa Touch has been no different. However, there are always some some cases where two things jut up against each other in odd ways.

I recently encountered an example of this: How do you build a UITableView combining the powers of both UISearchDisplayController and UILocalizedIndexedCollation? The documentation for each is excellent — particularly the TableSearch and TableViewSuite code samples — but I noticed no mention of how to combine the two à la the Contacts application.

The non-obvious bits are:

  1. How to display the small magnifying glass symbol.
  2. How to scroll the UISearchBar in to view when the magnifying glass symbol is tapped.
  3. How this integrates with UILocalizedIndexedCollation.

The first one is relatively simple: UITableView.h declares an NSString constant UITableViewIndexSearch. The second one takes a little thinking, but isn’t hard:

CGRect searchBarFrame = self.searchDisplayController.searchBar.frame;
[tableView scrollRectToVisible:searchBarFrame animated:NO];

Since a view’s frame is in the coordinates of its superview and since UISearchBar is inserted as a subview of UITableView (a subclass of UIScrollView), we can use this UIScrollView method to scroll the UISearchBar into place.

The last one is a little trickier, and is best explained with some code up front:1

// in sectionIndexTitlesForTableView:
return [[NSArray arrayWithObject:UITableViewIndexSearch] arrayByAddingObjectsFromArray:
        [[UILocalizedIndexedCollation currentCollation] sectionIndexTitles]];

// in tableView:sectionForSectionIndexTitle:atIndex:
if (title == UITableViewIndexSearch) {
    CGRect searchBarFrame = self.searchDisplayController.searchBar.frame;
    [tableView scrollRectToVisible:searchBarFrame animated:NO];
    return -1;
} else {
    UILocalizedIndexedCollation *currentCollation = [UILocalizedIndexedCollation currentCollation];
    return [currentCollation sectionForSectionIndexTitleAtIndex:index-1];

In sectionIndexTitlesForTableView: we construct an array that starts with UITableViewIndexSearch and is followed by the section index titles from the current collation.

In tableView:sectionForSectionIndexTitle:atIndex: we check the special case of UITableViewIndexSearch, handle it with a call to scrollRectToVisible:animated: as noted above. Returning -1 isn’t the greatest idea but for now UITableView seems to ignore it and in the future, if that changes, this will (hopefully) crash in a predictable way.2

If the user has in fact tapped on one of the letters, we subtract one from the index passed in and return the corresponding section. If that’s a little confusing, remember that we inserted UITableViewIndexSearch at the beginning of the section index titles array, so our indexes are off by one.

Sample code

It always helps me to see code in action, so I’ve modified the TableSearch code sample to include a section index.

It’s a real testament to the skill of the guys designing these APIs that this was that easy. Neither of these two APIs are fighting each other but instead are standing far enough apart that a developer can bridge the gap with clever bits of glue.

  1. I normally don’t use dot syntax, but the TableSearch sample code does. 

  2. I would expect an NSRangeException to be thrown. 


  1. Luke Redpath replied on February 23rd, 2010:

    I think I originally read this article a few months ago when I started playing around with UILocalizedIndexedCollation - thanks for that, it was quite useful.

    I’ve since written the same code a few times so I’ve extracted these modifications into a simple wrapper around UILocalizedIndexedCollation that takes care of this for you.

    Hopefully you’ll find it useful too: