18

I'd like to create custom UITableViewCell using an XIB, but I'm not sure how to recycle it using UITableViewController's queueing mechanism. How can I accomplish this?

Folks, this question was intended to be self answered as per the FAQ, although I love the awesome responses. Have some upvotes, treat yourself to a beer. I asked this because a friend asked me and I wanted to put it up on StackOverflow. If you have anything to contribute, by all means!

Community
  • 1
  • 1
Moshe
  • 57,511
  • 78
  • 272
  • 425

4 Answers4

51

If you're using iOS 5 you can use

[self.tableView registerNib:[UINib nibWithNibName:@"nibname" 
                                           bundle:nil] 
     forCellReuseIdentifier:@"cellIdentifier"];

Then whenever you call:

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

the tableview will either load the nib and give you a cell, or dequeue a cell for you!

The nib need only be a nib with a single tableviewcell defined inside of it!

Tony Million
  • 4,296
  • 24
  • 24
  • 2
    It was a new API introduced with iOS5 - been using it since the day I saw it - makes life so simple! You can even get more advanced with it by subclassing UITableViewCell and adding IBOutlet properties that point to the various controls in the cell, so you don't even need to use ViewWithTag any more! – Tony Million Feb 05 '12 at 16:46
  • Be careful, there's a bug in iOS 5 when VoiceOver is enabled. In this case `-dequeueReusableCellWithIdentifier:` always returns nil. See my workaround: http://stackoverflow.com/a/13881778/235297 – Ortwin Gentz Dec 14 '12 at 18:12
  • Theres a bug like this still present in iOS6 - I encountered it with VoiceOver a week ago - dequeue will return nil for both registered Nibs and Classes! – Tony Million Dec 18 '12 at 17:05
  • Is that bug fixed in IOS 6.1? – Warren P Apr 11 '13 at 15:14
  • Thank you for this answer, I was looking around and saw an answer involving loading an array of top level objects and mentioned it being in the docs. Your answer helped clarify that this was for pre-5.0 and after iOS 5 that's no longer needed. – Mark Reid Jul 13 '13 at 23:45
  • @TonyMillion how do you create the IBOutlets in this .xib file? In the "old method" you would have an actual class that represented your nib file. – El Guapo Jul 29 '13 at 17:56
2

Create an empty nib and add the table cell as the first item. In the inspector, you can add the reuseIdentifier string in Interface Builder.

To use the cell in your code, do this:

- (UITableViewCell *)tableView:(UITableView *)_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *reuseIdentifier = @"blah"; //should match what you've set in Interface Builder
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
    if (cell == nil)
    {
        cell = [[[NSBundle mainBundle] loadNibNamed:@"YourTableCellNib" owner:nil options:nil] objectAtIndex:0];
    }

    //set up cell

    return cell;
}

There is another method where you create an outlet for your cell and load the cell nib using the controller as the file's owner, but honestly this is much easier.

If you want to be able to access the subviews you've added to the cell in the nib, give them unique tags and access them with [cell viewWithTag:x];

If you want to be able to set custom properties on the cell, you'll need to create a custom UITableViewCell subclass, then just set that as the class of your nib in InterfaceBuilder and cast the UITableViewCell to your custom subclass when you dequeue it in the code above.

Nick Lockwood
  • 40,865
  • 11
  • 112
  • 103
2

To set up a custom UITableViewCell using a XIB, you have to do several things:

  • Set up an IBOutlet in your header
  • Configure the table view cell in Interface Builder
  • Load the XIB inside of tableView:cellForRowAtIndexPath:
  • Configure it like any other cell

So... Let's set up an IBOutlet in the header file.

@property (nonatomic, retain) IBOutlet UITableViewCell *dvarTorahCell;

Don't forget to synthesize it inside the implementation file.

@synthesize dvarTorahCell;

Now, let's create and configure the cell. You want to pay attention to the Cell Identifier and the IBOutlet as shown below:

enter image description here

Now in code, you load up the XIB into your cell as shown here:

enter image description here

Notice that the Cell Identifier in Interface Builder matches the one shown in the code below.

Then you go ahead and configure your cell like any other.

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {

    [[NSBundle mainBundle] loadNibNamed:@"YUOnlineCell" owner:self options:nil];
    cell = dvarTorahCell;
    dvarTorahCell = nil;
}

//configure your cell here.

Just note that when accessing subviews, such as labels, you now need to refer to them by tag, instead of by property names, such as textLabel and detailTextLabel.

Moshe
  • 57,511
  • 78
  • 272
  • 425
1

Here how you can do:

- (UITableViewCell *)tableView:(UITableView *)tableView 
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
  YourCustomeCell *cell = (YourCustomeCell *)[tableView dequeueReusableCellWithIdentifier:CellClassName];
  if (!cell)
  {
    NSArray *topLevelItems = [cellLoader instantiateWithOwner:self options:nil];
    cell = [topLevelItems objectAtIndex:0];
  }

  return cell;
}

Where cellLoader in .h is defined as follow:

UINib *cellLoader;

and in .m is istantiated as follows (for example during initialization):

cellLoader = [[UINib nibWithNibName:CellClassName bundle:[NSBundle mainBundle]] retain];

and CellClassName is the defined in .m as follows (is also the name for your xib).

static NSString *CellClassName = @"YourCustomeCell";

Do not forget to use the string CellClassName also in your xib created cell.

For further info I suggest you to read this fantastic tutorial creating-a-custom-uitableviewcell-in-ios-4.

Hope it helps.

P.S. I suggest you to use UINib because is an optimized method to load xib files.

Lorenzo B
  • 33,216
  • 24
  • 116
  • 190