10

我有 UITableViewController一个UISearchBar作为tableHeaderView它的tableView。我也有一个UISearchDisplayController初始化为 that UISearchBaras itssearchBar和 that UITableViewControlleras its contentsController。到目前为止一切顺利,几乎一切正常。

问题是UITableView具有accessoryType设置为的单元格UITableViewCellAccessoryDetailDisclosureButton。这是发生的事情:

  1. 首先,一切看起来都应该:
    在此处输入图像描述
  2. 用户在UISearchBar.
  3. 在主UISearchDisplayController表顶部创建深色覆盖,并使主表的索引(如,sectionIndexTitlesForTableView)消失。
  4. 假设此时用户隐藏了键盘(通过按 iPad 标准键盘上的“隐藏键盘”按钮)
  5. 由于用户尚未在其中输入任何内容UISearchBar,我们仍然可以看到主表,尽管位于由UISearchDisplayController.
  6. 键盘的隐藏暴露了更多的主表,导致主表加载更多的单元格。
  7. 现在问题来了:由于这些单元格是在主表的索引被隐藏时加载的,所以显示按钮显示得太靠右了(至少,与其他单元格相比)。
    在此处输入图像描述
  8. 此外,当用户现在取消搜索时,这些单元格可能不会重新加载,从而导致披露按钮显示在索引下方(现在再次可见)。
    在此处输入图像描述

我不知道如何解决这个问题;我能想到的唯一选择是找到与公开按钮相对应的 UIView 并手动移动它,但这似乎令人难以置信,因为即使发现 UIView 也需要一个讨厌的 hack。任何有关如何以更好的方式解决此问题的建议将不胜感激!

最小可运行示例

下面是一个最小的例子。只需启动一个新的 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;
    }
}
4

3 回答 3

1

如果您希望模仿 Apple 自己的应用程序在这些情况下的行为方式,那么正确的操作过程将是导致详细信息披露按钮在开始搜索时全部向右移动,然后再次全部向后移动一次搜索完成。

在您的示例中,我通过在您的代码中添加的两种方法调用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

但是这些会产生相当不和谐的效果,细节披露按钮的位置会突然跳动,所以我推荐前面提到的willBeginSearchand方法。willEndSearch

于 2013-02-02T10:36:15.440 回答
0

该问题不仅限于具有节索引标题的表。我在部分标题标题方面遇到了类似的问题。如果你添加

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

到您的“最小可运行示例”程序,然后您会观察到,一旦重新加载主表视图,部分标题标题也会出现在搜索表视图中。(这里报告了同样的问题:Removing Header Titles from a UITableView in Search Mode。)

我知道的唯一解决方案是避免对主表视图的所有更新,只要搜索显示控制器处于活动状态([self.searchDisplay isActive] == YES)并仅在卸载搜索表时重新加载主表视图:

- (void)searchDisplayController:(UISearchDisplayController *)controller  willUnloadSearchResultsTableView:(UITableView *)tableView {
        [[[self mainTableController] tableView] reloadData];
}
于 2013-02-02T09:46:48.660 回答
0

我能想到的最简单,也可能是最简洁的方法是告诉您的视图控制器(或视图)监听键盘事件。然后,当键盘最小化时,您退出搜索栏的第一响应者并重新加载您的 tableview(如果它还没有正确重新加载它)。

- (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 函数中辞去第一响应者(在键盘开始移动之前),因此您的tableview 单元格应该正确重新加载,而无需再次重新加载它们。

于 2013-02-01T20:32:28.257 回答