24

I am implementing a UITableView with UISearchDisplayController in xcode 4.2. UITableView & UISearchDisplayController are created in StoryBoard. I set the Cell Identifier (SampleCell) for UITableView and access it like

cell = [tableView dequeueReusableCellWithIdentifier:@"SampleCell"];

UItableView is working fine. But once i try to search, the app crash with below error.

*** Assertion failure in -[UISearchResultsTableView _createPreparedCellForGlobalRow:withIndexPath:], /SourceCache/UIKit_Sim/UIKit-1912.3/UITableView.m:6072
2011-11-09 22:22:16.058 SampleApp[14362:fb03] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:'

I guess I need to set the cell identifier for self.searchDisplayController.searchResultsTableView cell. But I don't know how. Thanks in advance for any help. =)

moon
  • 1,392
  • 3
  • 15
  • 29

4 Answers4

83

Use [self.tableView dequeue...], not [tableView dequeue...].

The cell you're trying to dequeue is linked to your primary view controller's tableView in the storyboard, NOT the searchDisplayController's newly created tableView (which has no cell identifiers linked to it). If you just message "tableView" then your dequeue message goes to the searchDisplayController's tableView since that's what was passed into the cellForRowAtIndexPath:... method.

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

    // do your thing

    return cell;
}
geekay
  • 1,655
  • 22
  • 31
hustoj2
  • 845
  • 6
  • 5
  • The new issue is that the search result is loaded into a popover that is not desired. can I somehow use my own (`self.tableView`) to display search results? – geekay May 02 '12 at 05:48
  • 1
    This is a very bad approach, as you are depending on another tableView instance. Replies by Cedrick and thejaz are much cleaner and definitely the way to go. – mgv Aug 05 '13 at 16:36
  • I'm experiencing issues when 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. Having the search table view use its own cells cache as suggested by others will fix this issue, however may require to manually setup segues and other stuff associated to the cells from the original table view. – CouchDeveloper Jun 27 '14 at 10:30
16

Hard bug to track indeed, it seems that every time you do a search a new tableview is created. Meaning that your cell registering has to be taken out of ViewDidLoad since this will only work for the first search. Instead use the following delegate method to do cell registering and customization:

    - (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller
{
    [self.searchDisplayController.searchResultsTableView 
     registerNib:[UINib nibWithNibName:@"YOURCELLNIB" bundle:nil] forCellReuseIdentifier:@"YOURCELLID"];
    self.searchDisplayController.searchResultsTableView.separatorColor = [UIColor clearColor];
}
Cedrick
  • 566
  • 3
  • 13
  • +1. I was dealing with a very similar problem and found that once I removed all calls to registerNib and replaced them with a different method of loading cells everything worked fine. – Mark Leonard Jan 24 '12 at 17:48
  • Do you know how to handle t his ios5 with stoaryboards? I created my own cell that extends UITableView and selected the Custom style in the storyboard. Everything works fine but when filtering the tablview the cell in cellforrowatindexpath is always nil so I get the error : In addition to the erro above I see *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath. How do I create that Custom cell I created in my storyboard programmatically? – imrank1 Jan 27 '12 at 13:38
  • Thank you, thank you, thank you!!! This was driving me crazy, since I had to necessarily register a nib for the searchResultsTableView cells, and putting the code in viewDidLoad was clearly wrong. If I could upvote your answer again I would do it! – Massimo Cafaro Dec 24 '12 at 18:33
  • THank you. I was just looking for a way to implement this solution but did not know which method searchdisplaycontroller calls when it is created. – zirinisp Jan 31 '13 at 13:55
  • Been scratching my head trying to debug this issue for a week. OMG THANKS. – 0xSina Aug 12 '13 at 07:06
10

(The answer that recommends using self.tableview is dependent on another table view. This is the cleanest solution and can be used even if the search controller is used by itself.)

You need to register the cells on the UISearchDisplayController's UITableView. The best way to do that is to register the cells when that table view is loaded.

UISearchDisplayDelegate has a method that notifies you when the table view is loaded - just like viewDidLoad but for the search table view.

- (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView
{
    [tableView registerNib:[UINib nibWithNibName:@"MyCellNib" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"MyCellIdentifier"];
}
thejaz
  • 2,763
  • 2
  • 26
  • 40
  • This is by far the best solution to avoid the crash after the first load! I mean, if you just would filter out the _already_ fetched data, the other way would work as expected. But if one should be able to do **remote searches**, the searchcontroller table view, should clearly not be dependent on the original table view. – Hulvej Aug 02 '13 at 14:36
  • 2
    Very helpful. See also: `registerClass: forCellReuseIdentifier:` if your cells are defined in code rather than Interface Builder. – Nick Nov 02 '13 at 07:56
0

You can in

- (void)searchDisplayController:(UISearchDisplayController *)searchDisplayController didLoadSearchResultsTableView:(UITableView *)searchResultsTableView

do

[searchResultsTableView registerNib:[UINib nibWithNibName:@"someNibName" bundle:nil] forCellReuseIdentifier:@"yourReuseId"];

Then you're able to use in

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

the normal

[tableView dequeueReusableCellWithIdentifier:

MacMark
  • 6,239
  • 2
  • 36
  • 41