11

So I have a tableView that has sections and rows, and it uses a custom cell class. The custom cell has an image view and a few labels. The table view works fine, and the search works, except the search does not display any of the labels that are in my custom cell class, only the imageView with the correct image. I am quite confused as to why this is, especially since the image is still displayed, but not the labels. Here is some code.

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


//TODO: problem with search view controller not displaying labels for the cell, needs fixing
JSBookCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

if(cell == nil) {
    cell = [[JSBookCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}

JSBook *book = nil;
//uses the appropriate array to pull the data from if a search has been performed
if(tableView == self.searchDisplayController.searchResultsTableView) {
    book = self.filteredTableData[(NSUInteger)indexPath.section][(NSUInteger)indexPath.row];
}
else {
    book = self.books[(NSUInteger)indexPath.section][(NSUInteger)indexPath.row];
}

FFMetaData *data = [self.ff metaDataForObj:book];
cell.titleLabel.text = book.title;
cell.priceLabel.text = [NSString stringWithFormat:@"$%@", book.price];
cell.authorLabel.text = book.author;
cell.descriptionLabel.text = book.description;

cell.dateLabel.text = [self.formatter stringFromDate:data.createdAt];
if(book.thumbnail == nil) {
    cell.imageView.image = [UIImage imageNamed:@"messages.png"];
    [self setCellImage:cell withBook:book atIndex:indexPath withTableView:tableView];
}
else {
    cell.imageView.image = [UIImage imageWithData:book.thumbnail];
}

return cell;

}

Before this problem, I had only one section in the tableView, and everything worked perfectly. Now that I have multiple sections and rows the search is broken as I described. Any ideas? Also, for [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; I used to have [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; But now if I use that I get a weird exception when I try to search:

NSInternalInconsistencyException', reason: 'request for rect at invalid index path ( 2 indexes [1, 1])'

So that is confusing me also. Thanks for the help!

terry lewis
  • 672
  • 1
  • 5
  • 13
  • Where did you make this cell? In a storyboard? In code? Did you register a nib or class? You should log cell, and see if you're getting a JSBookCell or a UITableViewCell (which has a default image view, which may be why you see that and not any of your labels). – rdelmar Apr 09 '13 at 04:27
  • The tableView and prototype cell are created in a storyboard. I registered the class like so: `[self.searchDisplayController.searchResultsTableView registerClass:[JSBookCell class] forCellReuseIdentifier:CellIdentifier];` I also logged the cell and I get this: > Which is the same in both normal and searchView controller. Though I also logged the titleLabel and it came out null for the search controller. – terry lewis Apr 09 '13 at 04:41

6 Answers6

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

did not work because table view cells are registered to a specific table view. This will not work for your search results controller table view. You did find this out yourself and switched to:

[tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

which is the right thing to do.

Also, designing your custom cell in storyboard will not really work for your search results controller because you are not able to design cells for search table view, only for the main table view.

Yes, you can register that class for your search table view, as you did here,

[self.searchDisplayController.searchResultsTableView registerClass:[JSBookCell class] forCellReuseIdentifier:CellIdentifier];

but that will not have any of the stuff you designed in your custom cell in storyboard. You would have to create all programmatically.

svena
  • 2,769
  • 20
  • 25
  • So basically I would just need to create another custom cell class for use in the search view? – terry lewis Apr 09 '13 at 12:35
  • 1
    You do not have to have two different classes. But you cannot really design them in storyboard as prototype cell for tableview. You can either create UI elements and layout of the custom cell programmatically in code or design in a separate nib, visually. – svena Apr 09 '13 at 12:52
  • 16
    Ok, I created a Xib and just copied my current cell into there and called `[self.searchDisplayController.searchResultsTableView registerNib:[UINib nibWithNibName:@"searchCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:CellIdentifier];` and it worked perfectly. Thanks! – terry lewis Apr 09 '13 at 14:12
  • 5
    @terrylewis, After a discussion with terry, I looked into this some more. What you (svena) say at the top is not correct. You can in fact use self.tableView, and if you want to design your cell in the storyboard, that's the way you want to do it (since that's the table view where that storyboard designed cell is, you have to get it from that table). The search display controller still uses its own table view, but it gets the cell from your main table view (via its identifier). I think the original problem had to do with sections, not the use of self.tableView. – rdelmar Apr 12 '13 at 06:03
  • @rdelmar You are half right about your conclusion. The original problem had to do with sections of course, because the indexPath that he was trying to dequeue cell for his search table view did not exist on the main table view, as you suggest. However, I do not agree that it is safe to dequeue a prototype cell from one table view and use it in another, unless you can point out a reliable source to say so. I have tested it and it seems to work, but it might break without warning. – svena Apr 12 '13 at 08:51
  • I can't find any documentation at all on this. The documentation on search display controller is a bit sparse. You may be right about it not being safe, however, I don't see why it shouldn't be -- the dequeuing process just grabs a cell from the storyboard, it isn't clear to me why the table view you make that cell in has any relevance other than you need it to find the cell in IB. I don't think the cell is tied to that table view in any way -- it has no reference to the table view, and the table view has no reference to it. I don't think they're associated with each other until you dequeue. – rdelmar Apr 12 '13 at 15:39
  • @rdelmar At least when you are using -dequeueReusableCellWithIdentifier:forIndexPath: class reference says: "The index path specifying the location of the cell. The data source receives this information when it is asked for the cell and should just pass it along. This method uses the index path to perform additional configuration based on the cell’s position in the table view." Also, it's a table view instance method, not class method. That is reference to table view enough for me to stay on the safe side. – svena Apr 12 '13 at 20:44
  • @svena - I agree with your point of view on whether it should be tableView or self.tableView. However, I was getting this error in my searchTableView even though I _had_ registered the class and identifier correctly. Switching to using self.tableView, despite the uncertain implications, fixes the issue. Very odd... – stephent Jul 11 '13 at 01:52
  • I encounter the same problem recently. And I have created a separented nib for searchRestultsTableView. and it worked. I just can't use the cell from storyboard. – yong ho Dec 16 '13 at 08:05
  • Disagree, I manage to archive that with only using Storyboard. Please have a look http://stackoverflow.com/questions/15892651/uisearchdisplaycontroller-not-correctly-displaying-custom-cells/20696418#20696418 – DazChong Dec 20 '13 at 04:44
  • 1
    I'd be wary of dequeuing from another table view. Without knowing exactly how a dequeue is implemented, I'd assume what could happen would be that the main table view creates a new instance of the cell, as there are no spare ones, then you're giving that to the search table view to manage. When asking for another cell, each time you dequeue from the main table view, it could be always creating new instances rather than reusing them, as each new instance would be managed by the search results table. Probably safest to use a separate NIB and dequeue from the table view requesting the cell. – Andrew Porritt Oct 27 '14 at 16:48
24

I got the same exception when I tried to search and some how this fixes it.

    if (tableView == self.searchDisplayController.searchResultsTableView) {
        cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    }else{
       cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];     
    }
harinsa
  • 3,176
  • 5
  • 33
  • 53
9

My summary for UITableView with Search Bar and Search Display using same custom cell designed in storyboard protype:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"CellIdentifierAsYouDefinedInStoryboard";

    CustomTableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil) {
        cell = [[CustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }

    if (tableView == self.searchDisplayController.searchResultsTableView) {
        /* searchResultsTableView code here */
    } else {
        /* Base tableView table code here */
    }

    /* more cell code here */

    return cell;
}


and then add this line for searchResultsTableView to match your custom cell height:

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.searchDisplayController.searchResultsTableView setRowHeight:self.tableView.rowHeight];

     /* more of your viewDidLoad code */
}
DazChong
  • 3,513
  • 27
  • 23
  • 2
    Great! Finally just what I needed. Big emphasis on "self.tableView dequeueReusableCellWithIdentifier" instead of any other table view, if you want to reuse the style. – webjunkie Sep 16 '14 at 13:41
  • 1
    This solution really work and should be the correct answer cuz it's easy and elegent. – Ali Dec 24 '14 at 06:49
6

If you are using a UITableView in a UIViewController and you want to reuse a Cell Identifier you created in your StoryBoard for your searchDisplayController, try this:

StoryBoard > UIViewController > Reference Outlets > link tableView to your UIViewController's .h file and call it something like tableView so you should have something like this:

@property (strong, nonatomic) IBOutlet UITableView *tableView;

So rather than doing it like this:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]
}

do this

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]
}
rharvey
  • 1,987
  • 1
  • 28
  • 23
0

If you create a cell in the storyboard you should not register the class (this actually screws things up). You register the class if you make the cell in code , and you register a nib if you make the cell in the nib. If you make it in the storyboard, you don't register anything, you use dequeueReusableCellWithIdentifier:forIndexPath:, and you don't need the if (cell == nil) clause at all.

rdelmar
  • 103,982
  • 12
  • 207
  • 218
  • If I don't register the cell it crashes, telling me I need to register the cell. – terry lewis Apr 09 '13 at 04:46
  • @terrylewis, are you using dequeueReusableCellWithIdentifier:forIndexPath:, not the shorter one? – rdelmar Apr 09 '13 at 04:47
  • I am using that, yes. `[tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];` – terry lewis Apr 09 '13 at 04:50
  • @terrylewis, also you'll need to add the identifier in the cellForRowAtIndexPath method if you don't define it somewhere else. – rdelmar Apr 09 '13 at 04:51
  • It is defined at the top of my .m as a static NSString. – terry lewis Apr 09 '13 at 04:54
  • @terrylewis, did you try logging cell (while you still had the register class method in place)? If not, you should do that to see if you're getting back the right class of cell. – rdelmar Apr 09 '13 at 04:56
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/27812/discussion-between-terry-lewis-and-rdelmar) – terry lewis Apr 09 '13 at 04:57
0

Try with this, it's work for me

JSBookCell * cell = [yourtableview dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
Bhavesh Odedra
  • 10,990
  • 12
  • 33
  • 58