【问题标题】:UITableViewCell detail disclosure button button too far right during search搜索期间 UITableViewCell 详细信息披露按钮按钮太靠右
【发布时间】:2013-01-14 15:20:56
【问题描述】:

我有UITableViewControllerUISearchBar 作为其tableViewtableHeaderView。我还有一个UISearchDisplayController 初始化为UISearchBar 作为它的searchBar 和那个UITableViewController 作为它的contentsController。到目前为止一切顺利,几乎一切正常。

问题在于UITableView 的单元格的accessoryType 设置为UITableViewCellAccessoryDetailDisclosureButton。以下是发生的事情:

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

我不知道如何解决这个问题;我能想到的唯一选择是找到与披露按钮相对应的 UIView 并手动移动它,但这似乎令人难以置信,因为即使找到了 UIView requires a nasty 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;
    }
}

【问题讨论】:

  • 请分享截图。
  • 请同时提供表格视图的代码。单元格创建/显示、搜索代码等...
  • 如果向下滚动,然后再次向上滚动(出现错误显示后,显示是否恢复正常?
  • 可能是一个愚蠢的问题,但是:为什么不直接删除黑色覆盖层并在关闭键盘时重新出现主表的索引?或者如果搜索查询为空,则在关闭键盘时重建列表?
  • @trumpetlicks:提供了一个最小的可运行示例。另外,是的,在取消搜索显示控制器并且我们滚动主表(使其重新加载这些单元格)之后,单元格再次正确构造。

标签: ios ipad uitableview uisearchbar


【解决方案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

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

【讨论】:

  • 啊啊。如此明显的解决方案。非常好,非常感谢。一旦 StackOverflow 允许(在 5 小时内),将奖励赏金。
【解决方案2】:

我能想到的最简单,也可能是最简洁的方法是告诉您的视图控制器(或视图)监听键盘事件。然后,当键盘最小化时,您退出搜索栏的第一响应者并重新加载您的 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 单元格应该可以正确重新加载,而无需再次重新加载它们。

【讨论】:

  • 问题不在于如何检测何时单元格构造不正确;例如,我可以检查搜索显示控制器是否处于活动状态。问题是做什么
  • “做什么”是重新加载表。似乎这个问题发生的唯一时间是当您的搜索栏仍然是第一响应者时键盘消失(keyboardwillhide)。解决这个问题的方法是让搜索栏在键盘消失时不是第一响应者。然后,当 tableview 重新加载时,它将正确定位所有内容,因为会出现侧面导航字母。
【解决方案3】:

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

- (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];
}

【讨论】:

  • 嗯,这真的很奇怪。即使使用完全独立的对象作为搜索表的数据源,我也证实了这一点。看来是改用主表的数据源了!
  • @edsko:我认为即使搜索处于活动状态,部分标题也会重新显示是一个错误。正如我所说,我不会在搜索处于活动状态时更新主表视图,并在卸载搜索表时重新加载它(如有必要)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-05
  • 1970-01-01
  • 1970-01-01
  • 2023-03-27
相关资源
最近更新 更多