将UISearchBar与表视图控制器一起使用并转换到另一个视图时显示问题

时间:2014-10-29 16:23:44

标签: ios cocoa-touch uisearchbar

自从iOS 8以来,我遇到了一个表格视图/ UISearchBar设置的奇怪问题,并想知道其他人是否遇到了类似的问题,或者可以指出什么,如果有的话,我可能做错了。广泛的情况:

  • 我有一个UITableViewController,里面有一个UISearchBar,在应用程序的Storyboard中设置
  • 表视图中有一个自定义单元格,再次在Storyboard中设置
  • 选择表格行会触发到另一个视图的segue
  • 执行搜索,从搜索结果中点击一行以切换到另一个视图,然后再次导航,触发各种问题。

“问题”是如果我按如下方式实现cellForRowAtIndexPath:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    MyCell *cell = (MyCell *) [self.tableView dequeueReusableCellWithIdentifier:@"MyId" forIndexPath:indexPath];
...

换句话说,通过指定dequeueReusableCellWithIdentifier的路径,这会导致iOS 8(但不是iOS 7)中的BAD_ACCESS或断言失败。具体来说,在上述情况下,在调用dequeueReusableCellWithIdentifier时发生断言失败或BAD_ACCESS,即,当搜索处于活动状态时,您从结果表中的一个单元格转移到另一个视图,然后再次转发。

现在,我可以通过调用:

来停止发生错误
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    MyCell *cell = (MyCell *) [self.tableView dequeueReusableCellWithIdentifier:@"MyId"];
...

不传入indexPath。这样就可以正常工作,但是在使用搜索结果回到表格视图时,会出现奇怪的显示问题,从而在搜索结果下面分层,似乎是“鬼”表的分隔符,几乎就像系统正在尝试将一个表直接呈现在另一个表之上(但是为每个表调用cellForRowAtIndexPath ,仅按预期为搜索结果表调用)。

我是否将segue附加到单元格或表视图控制器时遇到了同样的问题(因此在后一种情况下,我实现了didSelectRowAtIndexPath来手动触发segue)。

所以:(a)任何人都可以指出我可能做错的事情导致这些问题,或者(b)指向一个带有UISearchBar的表视图控制器的简单工作示例,其中表格单元格转到另一个视图?我很惊讶我遇到了很多问题,因为实现一个带有详细视图的可搜索表必须是一个人们常常做的常见,无聊的事情,不是吗?

展示iusse的示例项目: http://www.javamex.com/DL/TableTest.zip

4 个答案:

答案 0 :(得分:5)

我下载了你的代码,我观察到的是如果我改变了

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 

方法如下,它对我来说很好。

<强>解决方法1:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

    if (tableView == self.searchDisplayController.searchResultsTableView) {
        return [searchResults count];

    } else {
        return [testData count];

    }

}

请告诉我它是否适合您,我的建议是什么用途&#34; NSPredicate&#34;按如下方式过滤数组。

- (void) searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
    NSLog(@"Search text did change");

    NSPredicate *resultPredicate = [NSPredicate
                                    predicateWithFormat:@"SELF contains[cd] %@",
                                    searchText];

    searchResults = [testData filteredArrayUsingPredicate:resultPredicate];
}

Nlote:采取&#34; searchResults&#34;因为NSArray不是NSMutableArray。

以上解决方案对我有用这是另一种解决问题的方法。

<强>溶液2:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

    if (tableView == self.searchDisplayController.searchResultsTableView) {
        return [searchResults count];

    } else {
        return [testData count];

    }

}

- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
    NSPredicate *resultPredicate = [NSPredicate
                                    predicateWithFormat:@"SELF contains[cd] %@",
                                    searchText];

    searchResults = [testData filteredArrayUsingPredicate:resultPredicate];
}

// I implemented The following delegate method.


-(BOOL)searchDisplayController:(UISearchDisplayController *)controller
shouldReloadTableForSearchString:(NSString *)searchString {

    [self filterContentForSearchText:searchString scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
     objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];
    return YES;
}

答案 1 :(得分:2)

使用这两种方法为主tableView出列单元格实际上并没有错,尽管indexPath变体似乎是Apple最近的选择。

但是对于searchResultsTableView,请避免指定indexPath,因为它不一定对视图控制器的tableView有效。那就是:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    MyTableViewCell *cell;
    if ([tableView isEqual:self.searchDisplayController.searchResultsTableView]) {
        cell = [self.tableView dequeueReusableCellWithIdentifier:@"MyCell"];
    } else {
        cell = [self.tableView dequeueReusableCellWithIdentifier:@"MyCell" forIndexPath:indexPath];
    }
    // configure the cell
}

为了使其正常工作,您还需要修改其他UITableViewDataSource方法。而不是:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return (searchResults) ? (searchResults.count) : (testData.count);
}

执行:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    if ([tableView isEqual:self.searchDisplayController.searchResultsTableView]) {
        return searchResults.count;
    }
    return testData.count;
}

答案 2 :(得分:2)

实际上有两个tableView,UISearchResultsTableView用于显示结果,self.tableView用于显示源数据。

当您搜索&#34; One&#34;并清除所有搜索文本时,self.tableView中只剩下一个可见的单元格,UISearchResultsTableView中没有可见的单元格。实际上,此时两个表都没有可见的单元格。然后你搜索&#34; T&#34;,现在UISearchResultsTableView中应该有两个单元格,因为&#34;两个&#34;和&#34;三&#34;匹配&#34; T&#34;,此时,self.tableView中只有一个可见单元格,标签文本为&#34;一个&#34;,dequeueReusableCellWithIdentifier:forIndexPath适用于第0行,对于第1行,它崩溃了。我认为原因是self.tableView只有一个可见的单元格。

MyTableViewCell *cell;
if (tableView != self.tableView) {
    cell = [self.tableView dequeueReusableCellWithIdentifier:@"MyCell"];
} else {
    cell = [self.tableView dequeueReusableCellWithIdentifier:@"MyCell" forIndexPath:indexPath];
}

在textDidChange中,当searchText为空字符串时,在末尾添加此项以清除tableView。

if (searchText.length == 0) {
    [self.tableView reloadData];
}

答案 3 :(得分:2)

尝试了几种方法后,我的建议是避免与Apple API作斗争。

  除了拥有主表之外,

UISearchDisplayController管理它自己的UITableView 。筛选表中的单元格标识符与主表不匹配。

详情如下:UISearchDisplayController and UITableView prototype cell crash


因此,最好的方法是你已经发现的方法:

MyCell *cell = (MyCell *) [self.tableView dequeueReusableCellWithIdentifier:@"MyId"];

至于“鬼”表的分隔符问题,遗憾的是我无法重现,我会以这种方式处理它:

self.searchDisplayController.searchResultsTableView.separatorColor  = [UIColor clearColor];

祝你好运。