6

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.

Fengzhong Li
  • 111
  • 2
  • 9

5 Answers5

24

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.

Firoze Lafeer
  • 17,133
  • 4
  • 54
  • 48
  • Everything works fine now. The reason for calling [masterController init] is I found that masterController's init was not called at all, leaving NSMutableArray property uninitialized. I didn't aware that initWithCoder can do the same as init and it'll be called automatically during the process of storyboard's loading. And i need to learn more about the loading mechanism of storyboard/nib file. Thank you! – Fengzhong Li Mar 21 '12 at 05:20
  • This was driving me crazy all day. I was calling a custom init (old source prior to storyboards) from a source segue to the destination which was recreating the tableView just as shown here. Once I passed the values using the prepareForSegue method all of my reuseIdentifiers worked properly. Thank you Firoze! – Mark McCorkle Apr 25 '13 at 14:10
  • i had the same problem,and I instantiated my tableviewcontroller using the storyboard instatiateViewControllerWithIdentifier: and now everything works fine – Claus May 05 '13 at 18:52
  • I suffered the same issue. I had no idea that was the reason. Thank you! – Robert J. Clegg Apr 25 '14 at 11:07
  • Thanks for this, had a similar issue when trying to use UIViewController transitions. Forgot that presentViewController would not be loading the same storyboard instance. – StuartM May 13 '14 at 12:46
2

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)

canhazbits
  • 1,664
  • 1
  • 14
  • 19
1

Just in case it could help somebody in the future... In my case I forgot to change in TableView (Storyboard) Content from "Static cells" to "Dynamic Prototypes".

TomCobo
  • 2,886
  • 3
  • 25
  • 43
1

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?

fbernardo
  • 10,016
  • 3
  • 33
  • 46
  • I created a new project using the "Master-Detail Application" template provided by Xcode. Function dequeueReusableCellWithIdentifier did return something but nil when the very first cell is created. Why so? – Fengzhong Li Mar 21 '12 at 03:41
0

Typical cause:

[tableView dequeueReusableCellWithIdentifier: @"id"]; does not match Xcode > Attribute Inspector > Table View Cell > Identifier > id

SwiftArchitect
  • 47,376
  • 28
  • 140
  • 179