Wednesday, January 27th, 2010
All signs point to 2010 being Apple’s annus mirabilis.
The introduction of the iPad is only the beginning. WWDC10 looks like it’s going to be fantastic; typical release cycles seem to point where we’re likely going to see the release iPhone OS 4.0 along with a new iPhone, and the first tidbits of Mac OS X. Additionally, while we have seen something from the iWork team this year, we haven’t seen anything from the iLife team yet.
It’s also worth noting that Snow Leopard featured very few new features and interface changes; I highly doubt that Apple’s designers were paid to do nothing for the entire Snow Leopard cycle. I’m geared up for all kinds of crazy Mac OS X stuff this WWDC.
There’s also the usual array of new hardware announcements; a brief glance at the numbers makes me think we’re in for a wild, wild year on that front as well.
I’ve been following Apple news since the early ’90s1 and I can’t recall ever being as excited about what’s coming next as I am right now.
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:
- How to display the small magnifying glass symbol.
- How to scroll the
UISearchBar in to view when the magnifying glass symbol is tapped.
- 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.