Custom UITableViewCell
Creating the project
Create a new project with the “View-based Application” template and call it customTableCell.
Setting up the view controller
Open customTableCellViewController.h and add two IB outlets which we will use to reference to the custom UITableViewCell objects which we will create later. Also, we are going to be using this as a table view delegate and data source so we should state that this class will conform to these protocols. Your customTableCellViewController.h should look like this after editing:
#import @interface customTableCellViewController : UIViewController { IBOutlet UITableViewCell *cellOne; IBOutlet UITableViewCell *cellTwo; } @property (nonatomic, retain) IBOutlet UITableViewCell *cellOne; @property (nonatomic, retain) IBOutlet UITableViewCell *cellTwo; @end
Adding the items in Interface Builder
Open customTableCellViewController.xib in IB and drop in a Table View (not a Table View Controller), making it fill the view.
Set the the table view properties to Style -> Grouped.
Link the delegate and dataSource outlets of the table view to “File’s Owner” as illustrated by this screenshot:
![]()
Add two Table View Cell items in IB to your customTableCellViewController.xib file. Then, open each table view cell item and drop something different onto each one of them so you will tell them apart.
Now we need to link these table view cells to the IB outlets we created earlier. To do this, right click on “File’s Owner” and link cellOne and cellTwo outlets to the table view cells you have just created. You should then have something which looks like this:
![]()
Save and close customTableCellViewController.xib.
Controller Code
Open customTableViewController.m
We will need to implement three functions, namely tableView:cellForRowAtIndexPath:, numberOfSectionsInTableView: and tableView:numberOfRowsInSection: which are the usual data source protocol functions which we need to implement. The three functions are outlined below.
tableView:cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { if([indexPath row] == 0) return cellOne; if([indexPath row] == 1) return cellTwo; return nil; }
numberOfSectionsInTableView:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; }
tableView:numberOfRowsInSection:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return 2; }
These functions tell the table view to display 1 section, with 2 cells and when the table view asks for each cell, the custom UITableViewCell objects are returned.
And that’s it!
If you now build and run the application then you will notice that your custom cells are being shown – brilliant! Now you can do something fun with the custom cells! For instance you could put a text box or a slider in it and then bind actions of it to a function in your view controller to act upon the user’s input.
Go play!


That was a great tutorial, can i have a similar example but for implementing UIPickerView,
thank you.
Comment by Neha Gusain — November 28, 2008 @ 6:07 am
Many thanks! Certainly, I am going to try to release more tutorials soon and so I will look at UIPickerView and see what I can do. Glad you enjoyed the tutorial!
Comment by Matt — November 28, 2008 @ 7:40 am
thanks! this was the missing link!
Comment by te — November 29, 2008 @ 5:32 pm
[...] it was easy to make the code which handled the table to display the controls and passwords. I used UITableViewCells in Interface Builder to enable me to create the cells with a slider and the cell with [...]
Pingback by iPhone Apps » Blog Archive » QuickPass: An iPhone app in 2 hours 40 minutes! — December 2, 2008 @ 9:09 pm
I have added two items onto each of the table cell views. But when I right click on “File’s Owner” I don’t find link cellOne and cellTwo. Do I need to rename the cell views somehow? Or name the two items I dropped on (date picker and slider)?
Comment by tribune — December 2, 2008 @ 9:56 pm
@tribune – have you added the IBOutlet lines into the view controller header file in XCode and SAVED the file? Only after you’ve done that step will the links be available in Interface Builder.
Hope that helps!
Comment by Matt — December 2, 2008 @ 10:00 pm
Do you happen to know if it’s possible to use a UITableViewCell sublclass in only one row (or a subset of rows in a UITableView of otherwise standard cells?
Comment by GodOfBiscuits — December 5, 2008 @ 7:09 pm
You can yes. You just need to return the custom UITableViewCell for one row and a standard one for the other rows. E.g. inside the data source function:
NSInteger row = [indexPath row];
NSInteger section = [indexPath section];
if (section == 0) {
if (row == 0) {
return self.customcell;
}
else {
xxxxxxxx usual cell code xxxxxxxx
}
}
Comment by Matt — December 5, 2008 @ 8:17 pm
Hi,
I noticed that your code doesn’t call dequeueReusableCellWithIdentifier. Is your cell being instantiated for every row in cellForRowAtIndexPath?
Thanks,
Brendan
Comment by Brendan — December 8, 2008 @ 4:36 pm
Hi Brendan,
You don’t need to do that because you are returning the cell which is only ever initialized once (through the IBOutlet linkage). So essentially you are doing your own dequeue’ing by holding the cells yourself and then returning them as necessary. Make sense?
Matt
Comment by Matt — December 8, 2008 @ 4:42 pm
Thanks Matt,
Yes, this makes perfect sense. It’s odd though because I was just reading up on this topic in the iPhone SDK Development book by Agile Publishing and they talk about building a cell in Interface Builder, but then to retrieve the cell, they use a bunch of ugly code. They first subclass UITableViewCell, add a few properties to store the values, and then use the following code to retrieve the cell:
- (TeamCell*) createNewTeamCellFromNib {
NSArray* nibContents = [[NSBundle mainBundle]
loadNibNamed:@”TeamCell” owner:self options:NULL];
NSEnumerator *nibEnumerator = [nibContents objectEnumerator];
TeamCell* teamCell = NULL;
NSObject* nibItem = NULL;
while ( (nibItem = [nibEnumerator nextObject]) != NULL) {
if ( [nibItem isKindOfClass: [TeamCell class]]) {
teamCell = (TeamCell*) nibItem;
if ([teamCell.reuseIdentifier isEqualToString: @"Team"])
break; // we have a winner
else
teamCell = NULL;
}
}
return teamCell;
}
It would seem that using your technique, this would not be necessary.
Thanks,
Brendan
Comment by Brendan — December 8, 2008 @ 6:24 pm
That code seems so hugely complicated!
My way seems to work great for me. It’s slightly different in that I’m putting the table cells into the same NIB as the view which is using it, but so me, this made sense because it’s only this view which is going to use them. I can imagine that if you wanted to use the same cells across multiple views then my method wouldn’t work.
If you want to discuss more about it then feel free to email me: matt at galloway dot me dot uk.
Comment by Matt — December 8, 2008 @ 6:33 pm
The pieces of code do 2 different things, and should be used for different scenarios.
1. The code in the main article allows you to have only a single instance of the cell which simplifies the code. This is useful for when you have a fixed number of rows and you need only one of each kind.
2. The code that Brendan posted creates a new instance of the cell everytime the function is called. This allows you to treat the layout created in interface builder as a template for an arbitrary number of rows.
Comment by igor — December 24, 2008 @ 7:53 pm
That’s correct igor, my article is more for when you want a specific table cell for a specific task, although it’s easily adaptable to use it for multiple cells (as per what Brendan has suggested).
Comment by Matt — December 29, 2008 @ 6:27 pm