Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UITableViewCell detail disclosure button button too far right during search

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:

  1. To start, everything looks as it should:
    enter image description here
  2. The user taps inside the UISearchBar.
  3. The UISearchDisplayController creates the dark overlay on top of the main table, and makes the index (as in, sectionIndexTitlesForTableView) of the main table disappear.
  4. Suppose the user at this point hides the keyboard (by pressing the iPad "hide keyboard" button on the standard keyboard)
  5. Since the user hasn't typed anything into the UISearchBar yet, we can still see the main table, albeit underneath the dark overlay added by the UISearchDisplayController.
  6. The hiding of the keyboard exposes more of the main table, causing the main table to load more cells.
  7. Now here's the problem: Since these cells are loaded while the index of the main table is hidden, the disclosure button is shown too far too the right (at least, compared to the other cells).
    enter image description here
  8. Moreover, when the user now cancels the search, those cells may not be reloaded causing the disclosure button to be shown underneath the index (which is now visible again).
    enter image description here

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;
    }
}
like image 495
edsko Avatar asked Jan 30 '13 17:01

edsko


1 Answers

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.

like image 115
Trebor Avatar answered Nov 15 '22 22:11

Trebor