I have UITableViewController
with a UISearchBar
as the tableHeaderView
of its tableView
. I also have a UISearchDisplayController
initialized with that UISearchBar
as its searchBar
and that UITableViewController
as its contentsController
. So far so good, everything almost works.
The problem is that the UITableView
has cells which have their accessoryType
set to UITableViewCellAccessoryDetailDisclosureButton
. Here's what happens:
UISearchBar
.UISearchDisplayController
creates the dark overlay on top of the main table, and makes the index (as in, sectionIndexTitlesForTableView
) of the main table disappear.UISearchBar
yet, we can still see the main table, albeit underneath the dark overlay added by the UISearchDisplayController
.I'm at a loss on how to work around this; the only option I can think of is to find the UIView that corresponds to the disclosure button and manually move it, but that seems incredibly hacky, if only because even finding that UIView requires a nasty hack. Any suggestions on how to fix this in a nicer way would be much appreciated!
Minimal runnable example
Below is a minimal example. Just start a new XCode project, enable ARC, iPad only, and replace the contents of the AppDelegate with the below. Note that for the sake of the minimal example I force the main table to reload its cells in searchDisplayController:willShowSearchResultsTableView
, otherwise the main table will cache its cells and the problem won't show (in my actual application the main table is reloading its cells for others reasons, I'm not completely sure why -- but of course it should be fine for the main table to reload cells at any time.)
To see the problem happening, run the code, type something in the search box (you will see "Search result 0 .. 5") and then cancel the search. The disclosure buttons of the main table are now shown underneath, rather than beside, the index.
Below is just the code:
@interface AppDelegate ()
@property (nonatomic, strong) UITableViewController* mainTableController;
@property (nonatomic, strong) UISearchDisplayController* searchDisplay;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
UITableViewController* tableViewController = [[UITableViewController alloc] initWithStyle:UITableViewStylePlain];
UITableView* tableView = [tableViewController tableView];
[tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"Cell"];
[tableView setDataSource:self];
[self setMainTableController:tableViewController];
UISearchBar* searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 0, 44)]; // Width set automatically
[tableView setTableHeaderView:searchBar];
UISearchDisplayController* searchDisplay = [[UISearchDisplayController alloc] initWithSearchBar:searchBar
contentsController:tableViewController];
[searchDisplay setSearchResultsDataSource:self];
[searchDisplay setDelegate:self];
[self setSearchDisplay:searchDisplay];
[[self window] setRootViewController:tableViewController];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if (tableView != [[self searchDisplay] searchResultsTableView]) {
return 26;
} else {
return 1;
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView != [[self searchDisplay] searchResultsTableView]) {
return 10;
} else {
return 5;
}
}
- (void)searchDisplayController:(UISearchDisplayController *)controller willShowSearchResultsTableView:(UITableView *)tableView {
// The problem arises only if the main table view needs to reload its data
// In this minimal example, we force this to happen
[[[self mainTableController] tableView] reloadData];
[tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"SearchCell"];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView != [[self searchDisplay] searchResultsTableView]) {
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
[[cell textLabel] setText:[NSString stringWithFormat:@"%c%d", 'A' + [indexPath section], [indexPath row]]];
[cell setAccessoryType:UITableViewCellAccessoryDetailDisclosureButton];
return cell;
} else {
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"SearchCell" forIndexPath:indexPath];
[[cell textLabel] setText:[NSString stringWithFormat:@"Search result %d", [indexPath row]]];
[cell setAccessoryType:UITableViewCellAccessoryDetailDisclosureButton];
return cell;
}
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
if (tableView != [[self searchDisplay] searchResultsTableView]) {
return [NSArray arrayWithObjects:@"A", @"B", @"C", @"D", @"E", @"F", @"G", @"H", @"I", @"J", @"K", @"L", @"M", @"N", @"O", @"P", @"Q", @"R", @"S", @"T", @"U", @"V", @"W", @"X", @"Y", @"Z", nil];
} else {
return nil;
}
}
If you are looking to mimic the way Apple's own apps seem to behave under these circumstances, then the correct course of action would be the cause the detail disclosure buttons to all move to the right when starting the search and then to all move back again once the search is complete.
I have achieved this myself in your example by calling reloadData
on your main table view in two UISearchDisplayDelegate
methods which I added to your code:
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller
{
[[[self mainTableController] tableView] reloadData];
}
- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
[[[self mainTableController] tableView] reloadData];
}
This will force your detail disclosure views to be repositioned to take account of the visibility of the table view index, keeping the position of all disclosure indicators consistent with each other whether in search mode or not.
Update
I've toyed around with reloading the table view in other UISearchDisplayDelegate
methods including:
- (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView
- (void)searchDisplayController:(UISearchDisplayController *)controller willUnloadSearchResultsTableView:(UITableView *)tableView
But these produce a rather jarring effect with the positions of the detail disclosure buttons jumping around abruptly so I'd recommend the willBeginSearch
and willEndSearch
methods as previously stated.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With