Assertion failure when using UISearchDisplayController in UITableViewController

IosIos6UitableviewUisearchdisplaycontroller

Ios Problem Overview


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.

Ios Solutions


Solution 1 - Ios

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 - Ios

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 - Ios

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 - Ios

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.

Solution 5 - Ios

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

Solution 6 - Ios

I am working on that tutorial also. The default TableViewController has "forIndexPath" and in his example it doesn't exist. Once I removed it the search works.

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

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

Solution 7 - Ios

for swift 3 you just need to add self:

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

    ...
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionacib708View Question on Stackoverflow
Solution 1 - IosFunkyKatView Answer on Stackoverflow
Solution 2 - IosDanielView Answer on Stackoverflow
Solution 3 - IosaqubiView Answer on Stackoverflow
Solution 4 - IosSandro CavazzoniView Answer on Stackoverflow
Solution 5 - IosglorioView Answer on Stackoverflow
Solution 6 - IossonicbabblerView Answer on Stackoverflow
Solution 7 - IosEugene GordinView Answer on Stackoverflow