UITableViewControllerでUISearchDisplayControllerを使用するとアサーションが失敗する


80

アプリのTableViewControllerに簡単な検索機能を追加しようとしています。RayWenderlichのチュートリアルに従いました。いくつかのデータを含む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:メソッドを持つという目的全体に反しませんか?とにかく、私は迷子になっています。何が足りないのですか?助けてください。よろしくお願いします(:

アレックス。

回答:


194

dequeueReusableCellWithIdentifierでtableViewの代わりにself.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
それは、あるテーブルから別のテーブルに新しいセルを無期限にインスタンス化するだけではないでしょうか。あるテーブルからセルをデキューして別のテーブルに渡すのは悪い考えのように聞こえる場合
maltalef 2013年

1
これでもうまくいくと思いました。そうではありません。私の場合、検索テーブルのメインテーブルからセルをデキューしようとすると、クラッシュが発生することがあります。
リープロバート2013年

1
動作しません。検索してからキャンセルし(オーバーレイ結果モードを終了)、もう一度検索してみてください。クラッシュします。
ダニエル

8
提案された解決策はまだ私にとって例外を引き起こします。予想通り、私は交換した場合しかし、それは仕事に思えるdequeueReusableCellWithIdentifier:forIndexPath:ことでdequeueReusableCellWithIdentifier:
jweyrich 2013

3
ありがとうございました!tableviewの代わりにself.tableviewが問題でした!命の恩人!
adamteale 2014年

14

最初の実行ではうまく機能したが、結果テーブルを終了して別の検索に戻った場合にクラッシュした理由は、検索UITableViewモードに入るたびに検索ディスプレイコントローラーが新しいものを読み込んでいるためです。

検索モードとは、テキストフィールドをタップして入力を開始したことを意味します。その時点で、結果を表示するためのテーブルビューが生成され、キャンセルボタンを押すことでこのモードを終了します。テキストフィールドを2回タップしてもう一度入力を開始すると、2回目に「検索モード」に入ります。

したがって、クラッシュを回避するには、コントローラーメソッドではなくsearchDisplayController:didLoadSearchResultsTableView:デリゲートメソッド(from 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
残念ながら、これはストーリーボードの動的プロトタイプセルでは機能しません。
Ortwin Gentz 2014年

しかし、それはペン先によって登録されたセルでうまく機能します。良い回避策。
トーマスファーベーク2014年

12

検索後、cellForRowAtIndexPathメソッドの「tableView」は、定義したテーブルのインスタンスではないようです。したがって、セルを定義するテーブルのインスタンスを使用できます。の代わりに:

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
セルがストーリーボードでカスタムであり、クラスではなくセルからインスタンス化する必要がある場合、これは機能しません。
リープロバート2013年

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
残念ながら、これはストーリーボードの動的プロトタイプセルでは機能しません。
Ortwin Gentz 2014年

2

swift 3の場合は、自分自身を追加する必要があります。

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

    ...
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.