Кнопка кнопки раскрытия информации UITableViewCell слишком далеко справа во время поиска

У меня UITableViewController с UISearchBar как tableHeaderView его tableView. У меня также есть UISearchDisplayController, инициализированный тем, что UISearchBar как его searchBar, и UITableViewController как его contentsController. Пока все хорошо, все почти работает.

Проблема в том, что UITableView имеет ячейки, у которых их accessoryType установлен на UITableViewCellAccessoryDetailDisclosureButton. Вот что происходит:

  • Чтобы начать, все выглядит так, как должно:
    enter image description here
  • Пользователь забирает внутри UISearchBar.
  • UISearchDisplayController создает темную надпись поверх основной таблицы и уменьшает индекс (как в sectionIndexTitlesForTableView) основной таблицы.
  • Предположим, что пользователь в этот момент скрывает клавиатуру (нажав кнопку "Скрыть клавиатуру iPad" на стандартной клавиатуре)
  • Так как пользователь еще ничего не набрал в UISearchBar, мы все равно можем видеть основную таблицу, хотя и под темным наложением, добавленным UISearchDisplayController.
  • Скрытие клавиатуры предоставляет большую часть основной таблицы, заставляя основную таблицу загружать больше ячеек.
  • Теперь вот проблема: Так как эти ячейки загружаются, когда указатель главной таблицы скрыт, кнопка раскрытия отображается слишком далеко справа (по крайней мере, по сравнению с другими ячейками).
    enter image description here
  • Кроме того, когда пользователь отменяет поиск, эти ячейки не могут быть перезагружены, что приведет к тому, что кнопка раскрытия будет показана под индексом (который теперь снова отображается).
    enter image description here

Я не понимаю, как обойти это; единственный вариант, о котором я могу думать, - найти UIView, который соответствует кнопке раскрытия и вручную переместить его, но это кажется невероятно взломанным, хотя бы потому, что даже если найти UIView требует неприятного взлома. Любые предложения о том, как исправить это лучше, будут очень благодарны!

Минимальный пример runnable

Ниже приведен минимальный пример. Просто запустите новый проект XCode, включите только ARC, iPad и замените содержимое AppDelegate ниже. Обратите внимание, что для минимального примера я заставляю основную таблицу перезагружать свои ячейки в searchDisplayController:willShowSearchResultsTableView, в противном случае основная таблица будет кэшировать его ячейки, и проблема не будет отображаться (в моем фактическом приложении основная таблица перезагружает свои ячейки по другим причинам, я не совсем уверен, почему, но, конечно, должно быть хорошо, если основная таблица будет перезагружать ячейки в любое время.)

Чтобы увидеть проблему, запустите код, введите что-то в поле поиска (вы увидите "Результат поиска 0.. 5" ), а затем отмените поиск. Кнопки раскрытия основной таблицы теперь отображаются ниже, а не рядом с индексом.

Ниже приведен только код:

@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;
    }
}

Ответ 1

Если вы хотите подражать тому, как, по-видимому, ведут себя собственные приложения Apple, в этом случае правильный ход действий станет причиной того, что кнопки раскрытия подробностей all будут перемещаться вправо при запуске поиск, а затем все снова возвращаются после завершения поиска.

Я сам достиг этого в вашем примере, вызвав reloadData в вашем основном представлении таблицы в двух методах UISearchDisplayDelegate, которые я добавил в ваш код:

- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller
{
    [[[self mainTableController] tableView] reloadData];
}

- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
    [[[self mainTableController] tableView] reloadData];
}

Это заставит ваши представления раскрытия подробностей быть перемещенными, чтобы учесть видимость индекса таблицы, сохраняя положение всех индикаторов раскрытия, согласованных друг с другом, в режиме поиска или нет.

Обновление

Я играл с перезагрузкой табличного представления в других методах UISearchDisplayDelegate, включая:

- (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView
- (void)searchDisplayController:(UISearchDisplayController *)controller willUnloadSearchResultsTableView:(UITableView *)tableView

Но они создают довольно резкий эффект, когда позиции кнопок раскрытия подробностей резко скатываются, поэтому я рекомендую методы willBeginSearch и willEndSearch, как указано ранее.

Ответ 2

Самый простой и, возможно, самый чистый способ, который я могу придумать, - сообщить вашему диспетчеру просмотра (или представлению) прослушивание событий клавиатуры. Затем, когда клавиатура сведена к минимуму, вы уходите с первого ответчика на панель поиска и перезагружаете таблицу (если она не перезагружает ее должным образом).

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        //Your code here



        // register for keyboard notifications
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(keyboardWillShow:)
                                                     name:UIKeyboardWillShowNotification
                                                   object:self.window];
        // register for keyboard notifications
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(keyboardWillHide:)
                                                     name:UIKeyboardWillHideNotification
                                                   object:self.window];
    }
    return self;
}

-(void)dealloc
{


    [[NSNotificationCenter defaultCenter] removeObserver:self name: UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name: UIKeyboardWillHideNotification object:nil];
}

Затем добавьте эти две функции и сделайте то, что вам нужно.

- (void)keyboardWillHide:(NSNotification *)aNotification
{
    //searchbar resignFirstResponder

    //Tableview reload (if needed)
}

- (void)keyboardWillShow:(NSNotification *)aNotification
{
    //Don't really need this one for your needs
}

Поскольку вы отменяете первого ответчика в функции keyboardwillhide (до того, как клавиатура начинает двигаться), ваши ячейки таблицы должны перезагружаться должным образом, не перезагружая их снова.

Ответ 3

Проблема не ограничивается таблицами с заголовками индексов раздела. У меня была аналогичная проблема с заголовками заголовков разделов. Если вы добавите

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    if (tableView != [[self searchDisplay] searchResultsTableView]) {
        return [NSString stringWithFormat:@"Section %c", 'A' + section];
    } else {
        return nil;
    }
}

в программу "Минимальный исполняемый пример", то вы увидите, что заголовки заголовков разделов также отображаются в представлении таблицы поиска, как только перезагрузка основного табличного представления. (О той же проблеме сообщалось здесь: Удаление заголовков заголовков из UITableView в режиме поиска.)

Единственное решение, о котором я знаю, заключается в том, чтобы избежать всех обновлений в представлении основной таблицы до тех пор, пока активен контроллер отображения поиска ([self.searchDisplay isActive] == YES) и перезагружает представление главной таблицы только при выгрузке таблицы поиска:

- (void)searchDisplayController:(UISearchDisplayController *)controller  willUnloadSearchResultsTableView:(UITableView *)tableView {
        [[[self mainTableController] tableView] reloadData];
}