在UITableViewController中使用UISearchDisplayController时断言失败


80

我一直在尝试向我的应用程序中的TableViewController添加简单的搜索功能。我遵循了Ray Wenderlich的教程。我有一个带有一些数据的tableView,在情节提要中添加了搜索栏+显示控制器,然后有了以下代码:

#pragma mark - Table View
     - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BreedCell" forIndexPath:indexPath];

        //Create PetBreed Object and return corresponding breed from corresponding array
        PetBreed *petBreed = nil;

        if(tableView == self.searchDisplayController.searchResultsTableView)
            petBreed = [_filteredBreedsArray objectAtIndex:indexPath.row];
        else
            petBreed = [_breedsArray objectAtIndex:indexPath.row];

        cell.accessoryType  = UITableViewCellAccessoryDisclosureIndicator;
        cell.textLabel.text = petBreed.name;

        return cell;
    }

#pragma mark - Search
    -(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
        [_filteredBreedsArray removeAllObjects];
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.name contains[c] %@",searchString];
        _filteredBreedsArray = [[_breedsArray filteredArrayUsingPredicate:predicate] mutableCopy];

        return YES;
    }

    -(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption {
        // Tells the table data source to reload when scope bar selection changes

        [_filteredBreedsArray removeAllObjects];
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.name contains[c] %@",self.searchDisplayController.searchBar.text];
        _filteredBreedsArray = [[_breedsArray filteredArrayUsingPredicate:predicate] mutableCopy];
        return YES;
    }

标准的东西,但是当我在搜索栏中输入文本时,它每次都会因以下错误而崩溃:

2013-01-07 19:47:07.330 FindFeedo[3206:c07] *** Assertion failure in -[UISearchResultsTableView dequeueReusableCellWithIdentifier:forIndexPath:], /SourceCache/UIKit_Sim/UIKit-2372/UITableView.m:4460
2013-01-07 19:47:07.330 FindFeedo[3206:c07] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier BreedCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'

我了解到,在iOS 6中,单元的处理和出队系统发生了变化,并且搜索使用了不同的tableView,所以我认为问题在于带有过滤结果的搜索tableView不了解单元,所以我把这在我的viewDidLoad中:

[self.searchDisplayController.searchResultsTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"BreedCell"];

瞧!它有效...仅在您首次搜索时。如果返回原始结果并再次搜索,则该应用程序将崩溃,并显示相同的错误。我考虑过也许将所有

if(!cell){//init cell here};

填充到cellForRow方法中,但这是否与使用dequeueReusableCellWithIdentifier:forIndexPath:方法的整个目的背道而驰?无论如何,我迷路了。我想念什么?请帮忙。预先感谢您的所有时间(:

亚历克斯

Answers:


194

尝试在dequeueReusableCellWithIdentifier中使用self.tableView而不是tableView:

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

    //Create PetBreed Object and return corresponding breed from corresponding array
    PetBreed *petBreed = nil;

    if(tableView == self.searchDisplayController.searchResultsTableView)
        petBreed = [_filteredBreedsArray objectAtIndex:indexPath.row];
    else
        petBreed = [_breedsArray objectAtIndex:indexPath.row];

    cell.accessoryType  = UITableViewCellAccessoryDisclosureIndicator;
    cell.textLabel.text = petBreed.name;

    return cell;
}

这段代码效果很好

注意

如果您有自定义高度单元,请不要使用

[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

改用这个

[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];

4
那不会无限期地将一个表中的新单元格实例化吗?如果将一张桌子上的单元格从
队列中取出

1
我认为这也可以。没有。在我的情况下,尝试从我的主表中为搜索表中的单元出队有时会导致崩溃。
Lee Probert

1
不起作用 尝试搜索,然后取消(退出叠加结果模式),然后再次搜索。崩溃。
丹尼尔(Daniel)

8
提出的解决方案仍然给我带来了例外。然而,它似乎按预期方式工作,如果我取代dequeueReusableCellWithIdentifier:forIndexPath:dequeueReusableCellWithIdentifier:
jweyrich

3
谢谢!问题出在self.tableview而不是tableview上!救生员!
2014年

14

它在第一次运行时效果很好,但是如果退出结果表然后又返回另一个搜索时崩溃,则是因为UITableView每次进入搜索模式时,搜索显示控制器都会加载新的。

通过搜索模式,我的意思是,您点击了文本字段并开始键入内容,此时将生成一个表格视图以显示结果,并退出该模式,方法是单击“取消”按钮。当您第二次点击文本字段并再次开始键入时-这是第二次进入“搜索模式”。

因此,为了避免崩溃,您应该为表视图注册单元格类,以在searchDisplayController:didLoadSearchResultsTableView:委托方法(来自UISearchDisplayDelegate)中使用,而不是在控制器viewDidLoad方法中使用。

如下:

- (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView
{
    [tableView registerClass:[DPContentTableCell class] forCellReuseIdentifier:cellIdentifier];
    [tableView registerClass:[DPEmptyContentTableCell class] forCellReuseIdentifier:emptyCellIdentifier];
}

这让我感到惊讶,因为在iOS 7上...表视图被重用。因此,您可以根据需要注册课程viewDidLoad。出于遗留原因,我将使用我提到的委托方法保留注册。


2
不幸的是,这不适用于Storyboard动态原型单元。
Ortwin Gentz 2014年

但是它对笔尖注册的细胞非常有效。好的解决方法。
Thomas Verbeek

12

搜索后,cellForRowAtIndexPath方法的'tableView'似乎不是您定义的Table的实例。因此,您可以使用定义该单元格的表的实例。代替:

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];

使用:

UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];

(不要使用cellForRowAtIndexPath方法的tableView,请使用self.tableView。)


2
+1。在我快速实施此概念时需要self.tableView。
davidethell 2014年

3

在不使用'indexPath'的情况下使单元出队,如果获得nil元素,则必须手动分配它。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"YourCellId"];
    if (!cell)
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"YourCellId"];

    // fill your cell object with useful stuff :)

    return cell;
}

当您使用分段的主列表和普通搜索列表时,尝试使用self.tableView出队单元格可能会导致崩溃。该代码在任何情况下都可以工作。


1
如果您的单元是在情节提要中自定义的,需要从中实例化而不是在其类中实例化,则此方法将无效。
李·普罗伯特

3

当我遇到这个问题时,解决方案是将tableViewdequeueReusableCellWithIdentifier:@yourcell替换为self.tableView


2

我也在该教程上工作。默认的TableViewController具有“ forIndexPath”,在他的示例中不存在。一旦删除它,搜索就会起作用。

//Default code
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

//Replace with
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

1
不幸的是,这不适用于Storyboard动态原型单元。
Ortwin Gentz 2014年

2

快速3,您只需要添加self:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
    let cell = self.tableView.dequeueReusableCell(withIdentifier: "yourCell", for: indexPath) as! YourCell

    ...
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.