Using UILocalizedIndexedCollation with searchable table views

19 February 2010 Using UILocalizedIndexedCollation with searchable table views

On a recent FreeRange iPhone hack-day, we spent some time adding basic search functionality to a UITableViewController using some sample code provided by Apple. The sample code uses the UILocalizedIndexedCollation class to split an array of items into an array of arrays -- one array per section -- and display those accordingly.

This approach works fine, until you decide you want to add a search bar and the little magnifying glass symbol into your scrollable index -- just like Apple's own apps -- to go with it.

Whilst this is easy enough (all you need to do is return UITableViewIndexSearch as one of the elements in your section index titles array), this insertion of a new element into the section index means the section index passed into UILocalizedIndexedCollation needs to be offset by one to take this into account, for example:

- (NSInteger)sectionForSectionIndexTitleAtIndex:(NSInteger)indexTitleIndex
  if(indexTitleIndex == 0) { 
    return NSNotFound;
  return [collation sectionForSectionIndexTitleAtIndex:indexTitleIndex-1];

Whilst this is a relatively trivial fix, it would quickly become a pain if you have to do this everywhere you use a UITableView with a section index and a search bar. For that reason, I wrapped up UILocalizedIndexedCollation in a very simple decorator class which injects the magnifying glass icon into the section titles array and handles the offset for you.

The full code is available on a GitHub gist. It is designed as a direct drop-in replacement for UILocalizedIndexedCollation and as such implements the same interface. Simply swap out the calls to UILocalizedIndexedCollation in your existing delegate methods with calls to FRIndexedCollationWithSearch. The gist includes some sample usage.

One final tip: to have your UITableView scroll to the top (and reveal its header containing the search bar) when your user taps the magnifying glass icon in the search index, you can catch the NSNotFound returned by the collation in the tableView:sectionForSectionIndexTitle:atIndex delegate method:

- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
  NSInteger section = [[FRIndexedCollationWithSearch currentCollation] sectionForSectionIndexTitleAtIndex:index];
  if(section == NSNotFound) { 
    [tableView setContentOffset:CGPointMake(0,0)];
  return section;