将 UISearchBar 与表视图控制器一起使用并转到另一个视图时显示问题
Posted
技术标签:
【中文标题】将 UISearchBar 与表视图控制器一起使用并转到另一个视图时显示问题【英文标题】:Display issue when using UISearchBar with a table view controller and segueing to another view 【发布时间】:2014-10-29 16:23:44 【问题描述】:从 ios 8 开始,我在设置表格视图/UISearchBar 时遇到了一个奇怪的问题,想知道其他人是否遇到过类似的问题,或者可以指出我可能做错了什么,如果有的话。大体情况:
我有一个 UITableViewController,里面有一个 UISearchBar,在应用的 Storyboard 中设置 表格视图有一个自定义单元格,再次在情节提要中设置 选择表格行会触发转到另一个视图 执行搜索,点击搜索结果中的一行以转到另一个视图,然后再次导航回来,会触发各种问题。“问题”是,如果我按如下方式实现 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 没有为每个表调用,仅按预期用于搜索结果表)。
无论转场是附加到单元格还是表格视图控制器,我都会遇到同样的问题(所以在后一种情况下,我实现了 didSelectRowAtIndexPath 来手动触发转场)。
所以:(a) 任何人都可以指出我可能做错了什么导致这些问题,或者 (b) 指向带有 UISearchBar 的表格视图控制器的基本工作示例,其中表格单元格与另一个视图相连?我很惊讶我遇到了这么多问题,因为实现带有详细视图的可搜索表肯定是人们一直在做的一件常见而无聊的事情,不是吗?
展示 iusse 的示例项目: http://www.javamex.com/DL/TableTest.zip
【问题讨论】:
你能附上一个示例项目吗?我无法重现它。 上传了一个示例:如果您加载了应用程序,请搜索.e.g "One",然后单击搜索结果中该行上的信息图标以转到详细视图,然后返回,开始删除搜索词“一”输入其他内容,就会得到断言失败。 我无法重现第二个问题。我所做的是设置条件if (self.searchDisplayController.active) cell = [self.tableView dequeueReusableCellWithIdentifier:@"MyCell"]; else cell = [self.tableView dequeueReusableCellWithIdentifier:@"MyCell" forIndexPath:indexPath];
它运行良好
我已经下载了你的项目,看起来它工作正常!
【参考方案1】:
我下载了你的代码,我观察到如果我改变了
- (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];
请让我知道它是否适合您以及我的建议是使用“NSPredicate”来过滤数组,如下所示。
- (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:将“searchResults”作为 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;
【讨论】:
@Neil Coffey 是我的回答,对你有帮助。 你还面临这个问题吗【参考方案2】:虽然 indexPath 变体现在似乎是 Apple 的首选选项,但使用这两种方法将您的单元格从主 tableView
出列实际上并没有错。
但是,对于 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;
【讨论】:
【参考方案3】:其实tableView有两个,UISearchResultsTableView
用来展示结果,self.tableView
用来展示源数据。
当您搜索“一”并清除所有搜索文本时,self.tableView
中只剩下一个可见单元格,UISearchResultsTableView
中没有可见单元格。实际上此时两个表都应该没有可见的单元格。然后你搜索“T”,现在UISearchResultsTableView
中应该有两个单元格,因为“Two”和“Three”匹配“T”,此时self.tableView
中只有一个可见单元格,标签文本是"One",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];
【讨论】:
【参考方案4】:在尝试了几种方法后,我的建议是避免与 Apple API 对抗。
UISearchDisplayController 管理它自己的 UITableView,除了拥有你的主表。过滤表中的单元格标识符与您的主表不匹配。
详见此处:UISearchDisplayController and UITableView prototype cell crash
因此,最好的方法是您已经发现的方法:
MyCell *cell = (MyCell *) [self.tableView dequeueReusableCellWithIdentifier:@"MyId"];
至于“幽灵”表的分隔符问题,很遗憾我无法重现,我会以这种方式处理它:
self.searchDisplayController.searchResultsTableView.separatorColor = [UIColor clearColor];
祝你好运。
【讨论】:
以上是关于将 UISearchBar 与表视图控制器一起使用并转到另一个视图时显示问题的主要内容,如果未能解决你的问题,请参考以下文章
将 UIRefreshControl 与 UISearchBar 搜索结果一起使用
iPad表格视图上的UISearchBar消失在导航控制器栏下方