Sometimes the standard UITableViewCells don’t cut it and you need to create your own.
Here’s how you can create your own UITableViewCell in Xcode and format it however your like.
1. Create UITableView xib file.
After creating a xib project, and dragging in a UITableView

create a new xib file for your UITableViewCell

and call it CustomCell.xib.

Delete the view that it gives you and instead drag out a ‘Table View Cell’ from the utilities area.

Now, it’s in here where we are free to do whatever kind of layout we want. In this case I am going to layout two labels in the middle on top of each other, like this:

2. Create a custom class.
I am going to need some code to back this xib file up, so next we create an objective-c class that extends UITableViewCell.

and I am going to give it the same name as my xib – CustomCell.

3. Tie class to xib.
Now to we need to connect this class to the xib. We do by clicking on our cell xib Identity Inspector and typing in our class name as below:

4. Create some properties.
With those two connected, we now need to create some properties for the elements in our UITableViewCell xib. Do this like you would any other property, by control dragging the elements from the xib to the custom class.

Note: I also added a reuseIdentifier class method here with an implementation that looks like this:
CustomCell.m
+ (NSString *)reuseIdentifier { return @"CustomCellIdentifier"; }
We’ll use this later.
5. Tie it to the parent class.
Now we need to associate this xib with the parent VC xib that is going to use it. Do that by clicking the yellow cube on the left called ‘Files Owner’, the ‘Inspector’ tab in utilities, and then typing the VC file name (in this case I just used the default VC creating for me with the initial project).

6. Create a property for the cell in the parent.
In the ViewController with the UITableView that is going to be using this cell, add property pointing to it like so:
ViewController.h
#import <UIKit/UIKit.h> #import "CustomCell.h" @interface ViewController : UIViewController @property (assign, nonatomic) IBOutlet CustomCell *customCell; @end
ViewController.m
@synthesize customCell = _customCell;
Do this by bringing up your xib and the assistant for it, and dragging the cell over like any other control.
7. Connect this property to the cell.
This part is a bit weird, but now we go back to our custom cell xib, and connect our cell to the parent property in the View Controller.
Do this by clicking that ‘Files Owner’ yellow cube on the side, and then the ‘Connections Inspector’ circle with an arrow in it on the right. You should now see the customCell property we just defined in the parent VC.
Click on the little grey circle beside the customCell in the outlets section, and drag it to the cell. This connects the cell to the parent property.

8. Add a UITableView property to VC.
ViewController.h
@property (strong, nonatomic) IBOutlet UITableView *tableView;
ViewController.m
@synthesize tableView = _tableView;
9. Hook up UITableView datasource and delegate.
Last but not least we need to tell our UITableView that our ViewController is going to act as it’s datasource and delegate.
Go back to the ViewController xib, select the UITableView element, and from the ‘Connections Inspector’ drag those little circles out again beside dataSource and delegate to the yellow cube ‘Files Owner’ (not the UITableView!).

You will know you have it hooked up right when you see ‘File’s Owner’ beside the dataSource delegate outlets.
10. Implement the datasource delegate methods.
In your ViewController, implement the necessary UITableView datasource delegate methods.
ViewController.m
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return 1; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:[CustomCell reuseIdentifier]]; if (cell == nil) { [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:self options:nil]; cell = _customCell; _customCell = nil; } cell.topLabel.text = @"I am on top"; cell.bottomLabel.text = @"and I'm on the bottom"; return cell; }
10.b. Set cell reuse identifier
Update
As Ryan pointed out down in the comments section, we need to set the reuse identifier on our UITableViewCell, so it will get pooled and reused propertly in the pool.
Click on your table view cells, go to the attributes inspector and make sure the text you enter there matches the identifier in your UITableCell class. Else as Eric points out, our cells won’t get reused properly.
11. Fire it up!
Run the app in the simulator and you should now see something like this:

I also added one line to the viewDidLoad() to adjust for row height:
- (void)viewDidLoad { [super viewDidLoad]; // make row height same as our xib to get // rid of the appearance of multpile rows self.tableView.rowHeight = 125; }
Big thanks to Jeremy Gale for showing me the in’s and out’s of hooking up UITableViewCells to xibs.
Here’s a complete listing of files:
CustomCell.h
#import <UIKit/UIKit.h> @interface CustomCell : UITableViewCell + (NSString *)reuseIdentifier; @property (strong, nonatomic) IBOutlet UILabel *topLabel; @property (strong, nonatomic) IBOutlet UILabel *bottomLabel; @end
CustomCell.m
#import "CustomCell.h" @implementation CustomCell @synthesize topLabel; @synthesize bottomLabel; - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (self) { // Initialization code } return self; } - (void)setSelected:(BOOL)selected animated:(BOOL)animated { [super setSelected:selected animated:animated]; // Configure the view for the selected state } + (NSString *)reuseIdentifier { return @"CustomCellIdentifier"; } @end
ViewController.h
#import "CustomCell.h" @implementation CustomCell @synthesize topLabel; @synthesize bottomLabel; - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (self) { // Initialization code } return self; } - (void)setSelected:(BOOL)selected animated:(BOOL)animated { [super setSelected:selected animated:animated]; // Configure the view for the selected state } + (NSString *)reuseIdentifier { return @"CustomCellIdentifier"; } @end
ViewController.m
#import "ViewController.h" #import "CustomCell.h" @interface ViewController () @end @implementation ViewController @synthesize customCell = _customCell; @synthesize tableView = _tableView; - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return 1; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:[CustomCell reuseIdentifier]]; if (cell == nil) { [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:self options:nil]; cell = _customCell; _customCell = nil; } cell.topLabel.text = @"I am on top"; cell.bottomLabel.text = @"and I'm on the bottom"; return cell; } - (void)viewDidLoad { [super viewDidLoad]; // make row height same as our xib to get // rid of the appearance of multpile rows self.tableView.rowHeight = 125; } - (void)viewDidUnload { [self setTableView:nil]; [super viewDidUnload]; } @end
Bonus – how to create a new xib and ViewController
1. Create new xib.
2. Create new ViewController class.
3. Drag out display elements onto xib.
4. Connect the File Owner to the view.
We want the File Owner view outlet to be connected to the xib view.
Do this by clicking view owner, clicking the grey circle beside view, and then dragging the circle to the view on screen.
5. Connect display elements to File’s Owner
You need to connect each display element in your xib to your parent ViewController.
This is done by clicking the yellow cube ‘File’s Owner’ icon and dragging little grey circle beside it’s property to the yellow cube line.
When you are done you should be able to mouse over each outlet and see the corresponding element light up in your xib.
How to multiline label ios objective-c « The Agile Warrior
Jun 15, 2012 @ 13:12:22
Juan Pablo Balarini
Sep 04, 2012 @ 03:15:22
great tutorial!
hobeder
Oct 10, 2012 @ 09:13:20
this doesn’t work because you double wire the file owner with your cell. Remove that outlet and it will work!
JR
Oct 10, 2012 @ 12:51:50
If see what you are saying hobeder. I think you are right.
Thank you!
UITableView’s indexPathForCell: returns always 0 as indexPath.row
Oct 12, 2012 @ 13:03:26
Ryan
Oct 24, 2012 @ 07:47:11
Hm, I think there might be a problem with the reuseIdentifier. I don’t see where it ever gets set, so you’d never be able to dequeue a cell for re-use, meaning you’re forced to create a new one each time. I might be missing something though.
JR
Oct 24, 2012 @ 17:55:39
Good catch Ryan. You are correct. I forgot about that step.
I updated and added a 10b step which shows people how to do this.
Thank you for pointing that out.
Cheers – Jonathan
Balli Dar
Jan 30, 2013 @ 16:03:51
Hi Jonathan,
Tutorial is awesome, but i have same issue index path always return zero. I didn’t find solution please help me.
Thanks.
notarysojac
Nov 07, 2012 @ 20:11:36
Hey Jonathan – As they say in ‘Training Day’, “It’s all good, bro…”. Your very helpful tutorial performs just what I was hoping it would except my results are not behaving as I would wish.
What I get is a non-zero ‘CustomCell’ being returned by the call to “dequeueReusableCellWithIdentifier” – but the two label fields in the object are equal to ‘nil’. The result is that no text is drawn in the tableview call.
However – if I take the opportunity (in that same function) to set ‘cell’ via “loadNibNamed:” (as in your ‘cellForRowAtIndexPath’ routine) – then the label members are not ‘nil’ and the text appears in my tableview.
So – I went the the XIB and checked – and the two labels were, in fact, wired up to the ‘CustomCell’.
Any clues as to what is not properly hooked up? Thanks much!
notarysojac
Nov 07, 2012 @ 20:50:22
Hi Jonathan,
Acck! No sooner did I deposit my question then the answer dropped into my lap. As per my recent bouts with a UICollectionView object, I put a reference along the lines of UITableView’s ‘registerClass:forCellReuseIdentifier’ in the ‘viewDidLoad’ function of my ViewController object. – as in
[mytableview registerClass:[CustomView class]
forCellReuseIdentifier:@”MyCellReuseIdentifier”];
This was in addition to the XIB reference, as per your instructions. As soon as I removed that reference out of my code, suddenly it all started to work exactly as desired.
THANKS – and it’s STILL “All good, bro…”
JR
Nov 07, 2012 @ 23:17:28
Good to hear things worked out.
Glad the tutorial helped.
Keep on coding.
VansFannel
Feb 12, 2013 @ 09:29:36
Is it your ViewController.h code correct?
Another question: Have you corrected all issues that people have said on comments?
Thanks a lot for this useful tutorial.
VansFannel
Feb 12, 2013 @ 10:11:12
Why do you have to create a property for the cell in the parent? In ‘6. Create a property for the cell in the parent.’ you have created a property for the CustomCell and I think you use it in ViewController.m but I don’t know why you do this:
[[NSBundle mainBundle] loadNibNamed:@”CustomCell” owner:self options:nil];
cell = _customCell;
_customCell = nil;
Thanks
Dhananjay
Jun 27, 2014 @ 06:23:50
Greate Tutorial Worksss for me