Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Keep getting nil from dequeueReusableCellWithIdentifier?

I've created a prototype cell with identifier "mainViewTableCell" in storyboard file and connected the main table view with a custom controller class named "NTTableViewController". I've implemented function "tableView cellForRowAtIndexPath" in NTTableViewController.m as follows:


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString* MAINVIEW_CELLIDENTIFIER = @"mainViewTableCell";
    UITableViewCell *newCell = [tableView dequeueReusableCellWithIdentifier: MAINVIEW_CELLIDENTIFIER];

    if (newCell == nil) {
        newCell = [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleSubtitle reuseIdentifier: MAINVIEW_CELLIDENTIFIER];
        [newCell autorelease];
        newCell.selectionStyle = UITableViewCellSelectionStyleNone;
    }

    NTContactItem* currentItem = [self.contactItemContainer objectInContainerAtIndex: indexPath.row];
    NSString* firstName = currentItem.firstName;
    NSString* lastName = currentItem.lastName;

    NSString* fullName = [firstName stringByAppendingFormat: lastName];    
    [newCell.textLabel setText: fullName];
    [newCell.detailTextLabel setText: currentItem.mobilePhone];

    return newCell;
}

But i keeping getting nil from dequeueReusableCellWithIdentifier and have to create a new instance of cell every time.

Then, what is wrong?

The code : project

Thank you all in advance.

like image 613
Fengzhong Li Avatar asked Mar 21 '12 02:03

Fengzhong Li


3 Answers

With storyboards and tableviews that have prototype cells, [tableView dequeueReusableCellWithIdentifier:] should not return nil. Even if this is the very first cell, and there are no cells already in the reuse queue, the tableview will create a new instance of your prototype cell and return that.

In your case, the problem was something totally different (I downloaded your project because I was really curious).

In your application's delegate in your application:didFinishLaunchingWithOptions: method, you are re-initializing this tableviewcontroller. When you call [masterController init], this calls [super init], which in turn calls [UITableViewController initWithStyle:].

That causes the controller to create a new UITableView, which is different from the one in your storyboard. That new UITableView has no prototype cells, and so that's why dequeueReusableCellWithIdentifier: is returning nil.

The lesson of course is to not re-initialize an Objective-C object that has already been initialized. When your table view controller is loaded from the storyboard, the loading mechanism will initialize it with initWithCoder:. So if you need to do some custom initialization work (like setting up that NSMutableArray in your case), then just override initWithCoder: and/or awakeFromNib.

You can override these methods as needed, but do not call them yourself. Both initWithCoder: and awakeFromNib will be called by the Storyboard/nib loading mechanism.

If everything is correct, you do not need to create cells programmatically here. This bit of code should not be needed:

// This bit is unnecessary with storyboards:      
if (newCell == nil) {
    newCell = [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleSubtitle reuseIdentifier: MAINVIEW_CELLIDENTIFIER];
    [newCell autorelease];
    newCell.selectionStyle = UITableViewCellSelectionStyleNone;
}

Hope that helps.

like image 196
Firoze Lafeer Avatar answered Oct 20 '22 01:10

Firoze Lafeer


After reading @Firoze's answer I fixed my problem that had been haunting me for days.. and found another solution. I know the question has already been answered but this may help someone out.

In my case I had done some refactoring (extracted a new TableViewController), and afterwards I was getting nil cells returned from dequeueReusableCellWithIdentifier.

The fix for me was to do this instead of calling alloc/init on my ViewController in didSelectRowAtIndexPath:

UIStoryboard*  sb = [UIStoryboard storyboardWithName:@"MainStoryboard"
                                              bundle:nil];

MyTableViewController* controller = [sb instantiateViewControllerWithIdentifier:
                                    @"MyTableViewController"];

(Then I call [self.navigationController pushViewController:controller animated:YES]; - not the cleanest way perhaps but it works)

like image 21
canhazbits Avatar answered Oct 20 '22 01:10

canhazbits


Table cells only become reusable when they are hidden, so you should only have the number of cells you're currently displaying in memory. How many cells are you displaying, and how many are alloc'ed?

like image 36
fbernardo Avatar answered Oct 20 '22 03:10

fbernardo