Assertion failure when using UISearchDisplayController in UITableViewController

18,288

Solution 1

Try using self.tableView instead of tableView in dequeueReusableCellWithIdentifier:

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

This code works pretty well

Note

If you have custom height cells, do not use

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

Use this instead

[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];

Solution 2

The reason why it worked great on first run but then crashed if you exited the results table and went back in for another search is because the Search Display Controller is loading a new UITableView each time you enter search mode.

By search mode I mean, you've tapped the textfield and you've began to type, at which point a table view is generated to display results, exiting this mode it achieved by hitting the cancel button. When you tap the textfield the second time and begin typing again - this is entering "search mode" for the second time.

So in order to avoid the crash you should register the cell class for the table view to use in the searchDisplayController:didLoadSearchResultsTableView: delegate method (from UISearchDisplayDelegate) of instead of in your controllers viewDidLoad method.

As follows:

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

This caught me by surprise because on iOS 7... the table view is being reused. So you can register the class in viewDidLoad if you prefer. For legacy sakes, I'll keep my registration in the delegate method I mentioned.

Solution 3

After searching, 'tableView' of cellForRowAtIndexPath method seems not an instance of the Table that you define. So, you can use an instance of a table that defines the cell. Instead of:

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

Use:

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

(Do not use the tableView of cellForRowAtIndexPath method, use self.tableView.)

Solution 4

When I had this problem, the solution was replacing tableView dequeueReusableCellWithIdentifier:@yourcell with self.tableView

Solution 5

Dequeue the cell without using the 'indexPath' and in case of you obtain a nil element, you have to allocate it manually.

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

Trying to use self.tableView for dequeue the cell may cause crashes when you have a sectioned main list and a plain search list. This code instead work in any situation.

Share:
18,288

Related videos on Youtube

acib708
Author by

acib708

i write code

Updated on July 22, 2022

Comments

  • acib708
    acib708 almost 2 years

    I've been trying to add simple Search functionality to a TableViewController in my app. I followed Ray Wenderlich's tutorial. I have a tableView with some data, I added the search bar + display controller in storyboard, and then I have this code:

    #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;
        }
    

    The standard stuff, but when I enter text in the search bar it crashes every time with this error:

    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'
    

    I understand that in iOS 6 the handling and dequeueing system for cells changed, and also that the search uses a different tableView, so I thought the problem was that the search tableView with the filtered results didn't know about the cell, so I put this in my viewDidLoad:

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

    And voila! It worked... Only the first time you search. If you go back to the original results and search again, the app crashes with the same error. I thought about maybe adding all the

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

    stuff to the cellForRow method, but doesn't that go against the whole purpose of having the dequeueReusableCellWithIdentifier:forIndexPath: method? Anyway, I'm lost. What am I missing? Help, please. Thank you in advance for all your time (:

    Alex.

  • maltalef
    maltalef over 11 years
    wouldn't that just instantiate new cells from one table to another indefinitely? if dequeuing cells from one table and giving them to another sounds like a bad idea
  • FunkyKat
    FunkyKat about 11 years
    @figha no, this is ok. But if you want to see different cells for main table and search table, you can use different cellIdentifiers for different prototype cells in Storyboard
  • Lee Probert
    Lee Probert about 11 years
    I thought this would work too. It doesn't. In my case trying to dequeue the cell from my main table for the Search table sometimes causes a crash.
  • Lee Probert
    Lee Probert about 11 years
    This wont work if your cell is custom in the Storyboard and needs to be instantiated from it and not its class.
  • wcochran
    wcochran about 11 years
    Works like a charm! Pulls cells from correct NIB in storyboard. Of course this leads to the question: "what if I want to use different cells in the search controller's table view?"
  • Daniel
    Daniel almost 11 years
    Doesn't work. Try to search, then cancel (exit overlay results mode) and then search again. Crashes.
  • Daniel
    Daniel almost 11 years
    I found the correct solution, see my answer. stackoverflow.com/a/18228417/662605
  • jweyrich
    jweyrich almost 11 years
    The proposed solution still raises an exception for me. However, it seems to work as expected if I replace dequeueReusableCellWithIdentifier:forIndexPath: by dequeueReusableCellWithIdentifier:.
  • adamteale
    adamteale over 10 years
    thank you! self.tableview instead of tableview was the issue! life saver!
  • Ortwin Gentz
    Ortwin Gentz over 10 years
    Unfortunately this doesn't work with Storyboard dynamic prototype cells.
  • Ortwin Gentz
    Ortwin Gentz over 10 years
    The code raises a "request for rect at invalid index path" exception when requesting an indexPath that is not present in self.tableView. In most cases (when filtering the standard tableView) this is not an issue because the set of search indexPaths is a strict subset of the standard set of indexPaths. Nevertheless it's something to be aware of! For instance, you can't use sections in standard and omit them in the search results tableView. Unfortunately, this is - despite being ugly as @figha pointed out - the only solution I know of that works with Storyboard dynamic prototype cells.
  • Ortwin Gentz
    Ortwin Gentz over 10 years
    Unfortunately this doesn't work with Storyboard dynamic prototype cells.
  • Ortwin Gentz
    Ortwin Gentz over 10 years
    Addition to my previous comment: Of course this is a bug in iOS. I've logged openradar.me/16252999, if you want to dupe it.
  • jriggins
    jriggins about 10 years
    I don't think I would've ever figured that one out myself.
  • Thomas Verbeek
    Thomas Verbeek about 10 years
    But it works great with cells registered by nib. Good workaround.
  • CouchDeveloper
    CouchDeveloper about 10 years
    I'm too experiencing issues with this suggested solution where the search table view shares the reused cells with the original table view. UIKit (iOS 7.1) may log warning messages "no index path for table cell being reused". Later, it may crash do to accessing invalid indexPaths. It seems, the search table view must use its own cells cache. However this may require to manually setup segues and other stuff associated to this new cells, which were otherwise already setup in the cells associated to the original table view in a storyboard.
  • davidethell
    davidethell almost 10 years
    +1. Needed self.tableView in my swift implementation of this concept.
  • ravi
    ravi over 8 years
    I think its not safe to use self.tableView inside cellForRowAtIndexPath: method if you have searchDisplayController. Better use just tableView i.e the argument.
  • Ben Saufley
    Ben Saufley almost 8 years
    I've been searching left and right for the solution to this – with most people suggesting solutions that meant prototype cells generated in the Storyboard wouldn't work. And after all that … this is it. self. did it for me. Thank you. Thank you.