Moving the UITableViewIndex is SOLVED. The solution is insane, both because it uses some really cool dynamic method injection, and because it needs such trickery to work.
General method: We're going to add two methods to UITableViewIndex at runtime: one that makes the index move off screen, and one that makes it move back on screen.
Specific details:
1) In your application delegate's header file (YourAppDelegate.h), define two functions.
// for moving around the UITableViewIndex
static BOOL tableViewIndexMoveIn(id self, SEL _cmd);
static BOOL tableViewIndexMoveOut(id self, SEL _cmd);
2) In your application delegate's implementation file (YourAppDelegate.m), import the Objective-C runtime.
#import <objc/runtime.h>
3) Next add the following code to applicationDidFinishLaunching:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// dynamically add a method to UITableViewIndex that lets us move around the index
Class tvi = NSClassFromString(@"UITableViewIndex");
if ( class_addMethod(tvi, @selector(moveIndexIn), (IMP)tableViewIndexMoveIn, "v@:") ) {
NSLog(@"Added method moveIndexIn to UITableViewIndex");
} else {
NSLog(@"Error adding method moveIndexIn to UITableViewIndex");
}
if ( class_addMethod(tvi, @selector(moveIndexOut), (IMP)tableViewIndexMoveOut, "v@:") ) {
NSLog(@"Added method moveIndexIn to UITableViewIndex");
} else {
NSLog(@"Error adding method moveIndexIn to UITableViewIndex");
}
// do whatever else
return;
}
The magic happens with class_addMethod. This adds a selector to UITableViewIndex called moveIndexIn (note that it does not end with a colon because it doesn't take any arguments) with the implementation specified by tableViewIndexMoveIn. This is how we both identify which subview of UITableView is the index, and how we move the index offscreen & offscreen once we've found it.
4) Add the implementations of moveIndexOut and moveIndexIn to YourAppDelegate.m
#pragma mark UITableViewIndex Added Methods
static BOOL tableViewIndexMoveIn(id self, SEL _cmd) {
UIView *index = (UIView *)self;
[UIView beginAnimations:nil context:nil];
index.center = CGPointMake(index.center.x - 30, index.center.y);
[UIView commitAnimations];
return YES;
}
static BOOL tableViewIndexMoveOut(id self, SEL _cmd) {
UIView *index = (UIView *)self;
[UIView beginAnimations:nil context:nil];
index.center = CGPointMake(index.center.x + 30, index.center.y);
[UIView commitAnimations];
return YES;
}
5) Define a new header file called MovableTableViewIndex.h:
//
// MovableTableViewIndex.h
//
@interface MovableTableViewIndex
- (void)moveIndexIn;
- (void)moveIndexOut;
@end
6) In the class that implements the UISearchBarDelegate protocol (e.g. YourViewController.m), import the new header
#import "MovableTableViewIndex.h"
7) In this same class, add the code to hide the index when the search begins.
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)theSearchBar {
// move the index out of the way
for ( UIView *view in tableView.subviews ) {
if ( [view respondsToSelector:@selector(moveIndexOut)] ) {
MovableTableViewIndex *index = (MovableTableViewIndex *)view;
[index moveIndexOut];
}
}
// do whatever else
return YES;
}
Note that tableView is your UITableView.
8) Finally, add the code to show the index when the search ends.
- (BOOL)searchBarShouldEndEditing:(UISearchBar *)theSearchBar {
// move the index back
for ( UIView *view in tableView.subviews ) {
if ( [view respondsToSelector:@selector(moveIndexIn)] ) {
MovableTableViewIndex *index = (MovableTableViewIndex *)view;
[index moveIndexIn];
}
}
// do whatever else
return YES;
}
There may be other, more "proper" ways of doing this, but this method works for me, both in the simulator, and (more importantly) on the phone.
One subtlety is that in steps 7 and 8, I use respondsToSelector: to figure out which view is the UITableViewIndex. This is because calling className doesn't actually work on the phone, as in,
if ( [[view className] compare:@"UITableViewIndex"] == NSOrderedSame )
Now I just need to figure out how to shrink the width of the text field in the UISearchBar when the index is shown, and I'm all set!