20

I have a UIViewController setup in a storyboard with a tableview and UISearchDisplayController.

I am trying to use a custom prototype cell from self.tableview (which is connected to main tableview in the storyboard). It works fine if self.tableview had returned at least 1 cell when I load my view, but if self.tableview doesn't load a cell (as there is no data), and I load up the UISearchBar and search, the cellForRowAtIndexPath: method crashes:

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

    [self configureCell:cell atIndexPath:indexPath];
    return cell;
}

-(void)configureCell:(CustomSearchCell *)cell atIndexPath:(NSIndexPath *)indexPath {
    User *user = [self.fetchedResultsController objectAtIndexPath:indexPath];

    cell.nameLabel.text = user.username;
}

Errors:

*** Assertion failure in -[UITableViewRowData rectForRow:inSection:heightCanBeGuessed:]
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'request for rect at invalid index path (<NSIndexPath: 0x9ef3d00> {length = 2, path = 0 - 0})

My fetchedResultsController seems to have data (1 section, 2 rows) at the point the above method is called. It crashes on the dequeueReusableCellWithIdentifier line.

Any pointers/ideas? It should dequeue the prototype cell from self.tableview, but my guess is there was none created in self.tableview so this is the cause?

Rishil Patel
  • 1,977
  • 3
  • 14
  • 30
mootymoots
  • 4,545
  • 9
  • 46
  • 74

3 Answers3

60

UISearchDisplayController manages it's own UITableView (filtered table), in addition to having your primary table. The cell identifier in the filtered table doesn't match your primary table. You also want to fetch the cell not by indexPath as both tables can be vastly different from each other with respect to number of rows, etc.

So instead of doing this:

UITableViewCell *cell =
[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier
forIndexPath:indexPath];

instead do this:

UITableViewCell *cell =
[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
JOM
  • 8,139
  • 6
  • 78
  • 111
raw3d
  • 3,475
  • 1
  • 22
  • 25
  • 3
    I was struggling for a good 2 hours on this. Your response helped a lot, particularly your note that your omit forIndexPath because the tables could have vastly different indices. Also, for other readers: make sure that you have self.tableview not just tableview. – ThinkBonobo May 28 '14 at 15:45
  • SELF SELF SELF. using self like suggested here save my day. Thanks. It would be nice to know why, – zumzum Aug 04 '14 at 00:13
  • 4
    The reason why is because self.tableView asks for a cell from your view controller's table view, which has the Prototype cell set. Just using tableView uses the ivar passed in the delegate method, which points to the search controller's table, which does not have a Prototype cell set. Doing self.tableView gets the cell from your main table view, but then uses it in the search table view. – Cory Imdieke Oct 15 '14 at 18:34
  • But why does dropping the forIndexPath fix the crash? The mismatch indexPath makes sense, but then I thought I could do: [self.searchDisplayController.searchResultsTableView deque... forIndexPath:indexPath]; The tableView passed in to the delegate method is the searchResultsTableView. (Index path is 0 - 0). It still crashes, but works fine if I remove forIndexPath. – owenfi Nov 01 '14 at 09:59
  • 2
    @owenfi From the docs, the `...forIndexPath:` version *uses the index path to perform additional configuration based on the cell’s position in the table view.* It seems that the "additional configuration" is a problem when the index path doesn't make sense in terms of the table's contents, and would obviously be unwanted if the requested cell is going to be used in a completely different table (which is what happens with UISearchDisplayController). – Caleb Dec 19 '14 at 18:42
  • solved the problem exactly! searched for 20 minutes to find this answer. – horseshoe7 Apr 12 '15 at 19:02
7

I solved this by duplicating the prototype cell into a new xib:

In viewDidLoad:

[self.searchDisplayController.searchResultsTableView registerNib:[UINib nibWithNibName:@"CustomSearchCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"CustomSearchCell"];

And updated cellForRowAtIndexPath to use the method's tableview not the original self.tableview:

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

    [self configureCell:cell atIndexPath:indexPath];
    return cell;
}
mootymoots
  • 4,545
  • 9
  • 46
  • 74
-1

If you use à UISearchDisplayController in the methode celForRowAtIndexPath, you must use the tableView parameter methode and not the retain pointer of the controller.

try with this code

(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    CustomSearchCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CustomSearchCell" forIndexPath:indexPath];
tdelepine
  • 1,986
  • 1
  • 13
  • 19
  • 3
    CustomSearchCell identifier doesn't exist on the UISearchDisplayController cell. It's a prototype cell on the original tableview in the storyboard. You cannot add a prototype cell to UISearchDisplayController in the storyboard... AFAIK – mootymoots Sep 01 '13 at 16:30