Search code examples
iosobjective-cuitableviewuisearchdisplaycontroller

Custom UITableViewCell views are blank after searching


I believe this question is my exact problem, but I was not able to fix the issue looking at the accepted answer. UISearchBar: FilterContentForSearchText not working on a UITableView (results not shown on table)

I have a UITableViewController that allows searching. It was working perfectly via the default UITableViewCellStyle, then I decided to implement my own custom layout for the cells. I did not subclass UITableViewCell, but instead added two UILabels to the cell via Interface Builder and set up Auto Layout. I assigned them unique tags so that I can reference those labels in cellForRowAtIndexPath:. It works great - everything appears as expected, until you search. The search does work and it displays the sections and rows, but the labels on the rows have no text so the cell appears completely blank.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{
    static NSString *CellIdentifier = @"List View Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }

    UILabel *leftLabel = (UILabel *)[cell viewWithTag:100];
    UILabel *rightLabel = (UILabel *)[cell viewWithTag:101];

    if (tableView == self.searchDisplayController.searchResultsTableView) {
        leftLabel.text = ...;
        rightLabel.text = ...;
    }
    else {
        leftLabel.text = ...;
        rightLabel.text = ...;
    }
    return cell;
}

I do know why the labels are blank. When you search, dequeueReusableCellWithIdentifier: returns nil so it inits a new cell with the reuse identifier, but that cell does not have a viewWithTag: and this results in left and right label becoming nil. So it obviously cannot add text to nil.

The answer provided in the link stated you should create the label in the if (!cell) statement and then call [cell.contentView addSubview:left{right}Label];. I did that, and then moved my label configuration code into that if statement as well. But when I do that, the main table's rows only has the default values of my left and right labels from Storyboard - it doesn't set the text of the labels. This is because dequeueReusableCellWithIdentifier: doesn't return nil and instead creates a new cell, so it doesn't ever set the text because that's in the if (!cell) statement.

I could not figure out how to take care of both situations: when cell is nil and when it is not. What do I need to do to fix this?

More comments: I've never used xib files before and I'd prefer to keep it that way. :) I wouldn't mind subclassing UITableViewCell if that's a solution. Of course, I would like to implement this the "proper" way - only create a cell when one is needed etc.


Solution

  • I think the easiest way to do this is to make your cell in a xib file if you want to use the same cell type for both the main table and the search results table. You can make a subclass if you want (you only need to put in IBOutlets to your two labels in the .h file), or do it the same way you already did using tags. In viewDidLoad of the table view controller, register the nib for both tables,

    [self.searchDisplayController.searchResultsTableView registerNib:[UINib nibWithNibName:@"CommonCell" bundle:nil] forCellReuseIdentifier:@"CommonCell"];
    [self.tableView registerNib:[UINib nibWithNibName:@"CommonCell" bundle:nil] forCellReuseIdentifier:@"CommonCell"];
    

    Then in cellForRowAtIndexPath:, you only need to dequeue the cell with that same identifier, and populate the labels. There's no need to check for cell equals nil, because it never will be.

    I modified one of my apps to show how you can implement cellForRowAtIndexPath. I subclassed the cell (CommonCell is the class), only adding IBOutlets to the leftLabel, and rightLabel,

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        CommonCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CommonCell" forIndexPath:indexPath];
        cell.leftLabel.text = ([tableView isEqual:self.tableView])? self.theData[indexPath.row] : self.filteredData[indexPath.row];
        cell.rightLabel.text = ([tableView isEqual:self.tableView])? self.theData[indexPath.row] : self.filteredData[indexPath.row];
        return cell;
    }