iOS Programming 101: Customize UITableView and UITableViewCell Background using Storyboard
In the last tutorial of our iOS Programming 101 series, we showed you how to customize the navigation bar and buttons with your own background image. This time, we’ll look into the customization and styling of UITableView and UITableViewCell.
If you’ve followed our iOS tutorials from the very beginning, you know we’ve written a tutorial about customizing table view cell using Interface Builder. In this tutorial, we’ll do something different. Instead of using Interface Builder to tweak the UITableViewCell, we’ll show you how to use Storyboards to style the cell. Storyboards make customizing table cells much easier with the introduction of prototype cell. In brief, you’ll learn the following stuffs in this tutorial:
- Customizing UITableViewCell using Storyboard
- Style UITableViewCell and UITableView with your own background images
To illustrate the customization, we’ll tweak a simple table view, style it and make it more attractive. To help you focus on learning the customization, we’ve prepared the Xcode project for you to start with. Before proceeding, first download the Xcode project here (note: the project is created using Xcode 4.5).
Designing Prototype Cells in Storyboard
Since the release of iOS 5, Apple introduced Storyboarding that simplifies the way to design user interfaces for your app. To customize a table cell, you no longer need to create a separate cell in Interface Builder. You can design the cell right inside the Storyboard editor.
Open the Xcode project you’ve downloaded. Go to the Storyboard and you should see something like this:

Table View Controller with Empty Prototype Cell
When you add a UITableViewController in the Story, by default, you should see an empty prototype cell. You can now add other UI control elements (e.g. UILabel, UIImageView) right into the prototype cell. Let’s first change the height of the cell. Select the Prototype Cells, click the “Size” inspector and change the row height from 44 to 71.

Changing row height in Storyboard
Next, drag an Image View from object library to the prototype cell. Again, select the image view, click “Size” inspector and change the “X”, “Y”, “Width” and “Height” attributes as follows:

Add image view to prototype cell
Switch to the “Attributes” inspector, set the “Tag” to 100. You can think of a tag as an unique identifier of the UI control. Later in our code, we’ll use this tag to identify the image view.

Assigning a tag for the image view
Then drag a label to the cell and name it as “RecipeName”. This label is used to display the name of recipe. In the “Size” inspector, set “X” as 92, “Y” as 7, “Width” as 186 and “Height” as 21. In the “Attributes” inspector, change the font type to “Helvetica Neue Condensed Bold” and set the font size to 21 points. Finally, set the “Tag” value to 101.

Add labels to the prototype cell
Add another label to the cell and name it as “Details”. In the “Size” inspector, set “X” as 92, “Y” as 32, “Width” as 186 and “Height” as 33. Switch to the “Attributes” inspector, set the “Tag” value as 102.
Coding the Table View Controller
With the user interface designed, let’s get back to the code. Open “RecipeViewController.m”, which is the corresponding class of the Table View Controller, change the “cellForRowAtIndexPath:” method to the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; // Configure the cell... if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; } // Display recipe in the table cell Recipe *recipe = [recipes objectAtIndex:indexPath.row]; UIImageView *recipeImageView = (UIImageView *)[cell viewWithTag:100]; recipeImageView.image = [UIImage imageNamed:recipe.imageFile]; UILabel *recipeNameLabel = (UILabel *)[cell viewWithTag:101]; recipeNameLabel.text = recipe.name; UILabel *recipeDetailLabel = (UILabel *)[cell viewWithTag:102]; recipeDetailLabel.text = recipe.detail; return cell; } |
We’ve added the code (from line 12 to 20) for displaying the cell labels and image view. As you can see from the code, we invoke the “viewWithTag:” method with the tag value to identify the UI component.
Compile and run the app. Your app should look like this:

Recipe Book App with default cell style
Styling the UITableCell with Background Image
Now you’ve learnt how to create custom table cell using Storyboard, but the table view cell is just in plain style. We want to change the look & feel of the table cells and make them prettier. In the Xcode project, you should find three background images for table cell. The images are specially designed for different types of cells.

Cell Background Image – Top and Bottom images are with rounded corners
The UITableViewCell class allows developers to assign a custom background view using the “backgroundView:” method. To assign your own custom background image, we can create an UIImageView object with the background image and set it as the background view of the cell. Add the following code to the “cellForRowAtIndexPath:” method:
1 2 3 4 5 6 |
// Assign our own background image for the cell UIImage *background = [self cellBackgroundForRowAtIndexPath:indexPath]; UIImageView *cellBackgroundView = [[UIImageView alloc] initWithImage:background]; cellBackgroundView.image = background; cell.backgroundView = cellBackgroundView; |
There is one thing missing. We haven’t implemented the “cellBackgroundForRowAtIndexPath:” method. As explained before, we have three types of background image. The top and bottom cells are assigned with different background images that are differed from the rest of the cells. So we create the “cellBackgroundForRowAtIndexPath:” method to determine the background image to assign. Add the following code before the “cellForRowAtIndexPath:” method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
- (UIImage *)cellBackgroundForRowAtIndexPath:(NSIndexPath *)indexPath { NSInteger rowCount = [self tableView:[self tableView] numberOfRowsInSection:0]; NSInteger rowIndex = indexPath.row; UIImage *background = nil; if (rowIndex == 0) { background = [UIImage imageNamed:@"cell_top.png"]; } else if (rowIndex == rowCount - 1) { background = [UIImage imageNamed:@"cell_bottom.png"]; } else { background = [UIImage imageNamed:@"cell_middle.png"]; } return background; } |
For the first row of the cell (i.e. rowIndex == 0), the method returns the top cell image. If it’s the last table cell (i.e. rowIndex == rowCount – 1), the method returns the bottom cell image. For the rest of table cell, it simply returns the middle cell image.
Lastly, add the following line of code in the “viewDidLoad” method of RecipeViewController.m. This tells the table cell not to show the separator as our own cell images already come with the separator.
1 |
[self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleNone]; |
Compile and run the app. You should end up with an app like this:

Recipe book app with custom background cell image
Styling the Table View Background
The app looks much better, right? Let’s make it even better by assigning a background image to the view. Add the following code to “viewDidLoad” method of RecipeViewController.m:
1 2 |
self.parentViewController.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"common_bg"]]; self.tableView.backgroundColor = [UIColor clearColor]; |
As the RecipeViewController is a subclass of UITableViewController, we use “self.parentViewController” to access its parent view, followed by assigning the background. By default, the table view is displayed in white. We set its color to transparent so as not to block out the background of the parent view.
Before you run the app, we’d like to make one more change. The table view is now very close to the navigation bar. You may want to add padding at the top of the table view. To add padding, you can use the contentInset property to specify a buffer area around the content of the view. The contentInset property is a UIEdgeInsets struct with the fields top, bottom, left, right. Let’s add the following code in “viewDidLoad”:
1 2 |
UIEdgeInsets inset = UIEdgeInsetsMake(5, 0, 0, 0); self.tableView.contentInset = inset; |
The above code results in an additional buffer area that is 5 pixels at the top of the table view. Compile and run the app again. Your app should now be customized with padding and our own background image:

Recipe book app in better style
What’s Coming Next
Again, I hope you enjoy the tutorial and find it useful. For your complete reference, you can download the final Xcode project here.
Earlier, we already showed you how to customize the tab bar and navigation bar. Combining with this tutorial, you should be able to make your table-based app more attractive. Later, we’ll see how to style other UI elements. Stay tuned.
What do you think about this tutorial? As always, leave us comment and share your thought.
Comments
kevin chen
AuthorWonderful, It is useful for me, thanks!
Alex Ribeiro
AuthorAmazing! I love your tutorials! I wish you could tell us how to implement a facebook-like sidebar slide menu… I’d like to see it here, step by step, like you always do! Please! 🙂
Marcos Vilela
Authorvery nice tutorial! thank you very much.
Jake Scott
AuthorYou might want to specify what the font size for the details label?
Jake Scott
AuthorAlso can’t you simplify the cell background image code to:
UIImage *backgroundImage = nil;
if (indexPath.row == 0) {
backgroundImage = [UIImage imageNamed:@”cell_top.png”];
} else if (indexPath.row == recipes.count – 1) {
backgroundImage = [UIImage imageNamed:@”cell_bottom.png”];
} else {
backgroundImage = [UIImage imageNamed:@”cell_middle.png”];
}
MacUser
AuthorGreat Tutorials thank you for taking the time to make these
xephersteel
Authornice tutorial!! Could u explain more about the UIEdgeInsets stuff though? Quite new stuff 🙂
Eli
AuthorIf you add an Edit Button, the cell contents will not shift to the right when you enter editing mode. Is there a way to fix this?
e.g.
– (void)viewDidLoad {
…
self.navigationItem.leftBarButtonItem = self.editButtonItem;
}
peter
AuthorHey! Did you ever find a solution for that? Me: not so much.
Raag
AuthorHey Simon, thanks for the awesome tutorials you prepare for us. They have helped me a lot. I had an off topic question. I was doing my internship in a company where I had access to Mac, and thus could make iOS applications. But currently I am at home, and don’t have an access to a Macintosh. Buying a Mac doesn’t look feasible as I already have a Windows machine. Do you know is there a way/work around so that I can make iOS applications on a Windows platform? Sorry for a question irrelevant to the topic.Thank you.
Simon Ng
AuthorThere is no perfect solution to develop apps on Windows platform. You can check out this thread on stackoverflow:
http://stackoverflow.com/questions/22358/how-can-i-develop-for-iphone-using-a-windows-development-machine
Another solution is to rent a Mac so that you can remote access it. Here are two sites that you can check out:
http://www.xcodeclub.com/
http://www.macincloud.com
Albert
AuthorHi Simon, thanks for the great tutorial series so far! I was wondering if you could do a tutorial about how you can perform SQL queries (fetch data from the database) using the search bar? For example, for a phonebook style app, a user taps on the search bar to search for a contact. Specific details from the database can then be fetched and put under different categories e.g. like the Table View for recipes with a disclosure icon so when a user taps on say “Address” cell, it takes them to a new scene with their address (loaded from the database). I hope that made some sense. Thanks!
Colin
AuthorHi Simon, this tutorial was great! I am putting my first iPhone app together (for fun) and was wondering if you are ok with other people using the background images used in the tutorial in their apps?
Looking forward to checking out more of your tutorials! Cheers.
Simon Ng
AuthorYes, feel free to use it. But if you want to share the images with others, please share this post with them.
Yuri Koller
Authorwhen I tried to compile and run the app the first time that you say to do so, instead of it running, this pops up (i think it’s the debugger window or something. I don’t know if it has anything to do with the fact that I’m using Xcode 4.6 instead of 4.5
Varun Iyer
AuthorI have the same problem too please help!!!
J Mayu
Authorno point of posting this screenshot. What did you get in logs?
Saleh Masum
AuthorI wanna say just one word ‘beautiful’ 🙂 I really like this tutorial. I’ve been working on table view with no design just plain. Now my all designs related to table view would be so cool !! .. thanx for the beautiful work 🙂
EA
AuthorVery good tutorial. But how can I change the height of the row and the recipeNameLabel automatically to see the full name of recipe?
Osvaldo Cipriano
AuthorThe height row just go to “Show the size inspector” and change there the size. To show full name of the recipe you just need to put two lines in the attribute inspector.
Hoped that help you.
Glenn Barnett
AuthorThanks for the tutorial. A few problems I encountered:
1.) In the Storyboard, I had to assign my Table View Cell a specific identifier (e.g. “Cell”) so it would match the dequeue call in cellForRowAtIndexPath.
2.) Also in the Storyboard, setting the custom height on the cell alone was insufficient; I had to also set the custom height on the tableview itself to get it to work at runtime.
Jeremy
AuthorBoth of these points were supremely helpful. If the identifier and the dequeue call don’t match, all you get are blank cells.
paco
AuthorI knew it ! I knew that I have to do it !!! 3 hours trying to find the problem and the solution was at the first comment, THANK YOU very much !!!!
Akshay Taru
AuthorHow do set custom height on the tableview? Please tell me I am newbie in iPhone development
Murtaza J Masalawala
AuthorHello mate, i tried both your tips but my tableViewCells are still blank. Really pulling my hair out ! You got any more bright ideas?
Osvaldo Cipriano
AuthorDoes anyone knows how to use a plist to show the data instead of putting in the code? It would be great so we could update the recipes.
Simon Ng
AuthorYou can check out the Property List tutorial:
http://104.131.120.244/enhance-your-simple-table-app-with-property-list/
Osvaldo Cipriano
AuthorCan you share the code of that tutorial?
Dave
AuthorIs there any way to modify the width of the blue highlight when the cell is tapped?
The cell looks smaller in width because of the image but when it’s tapped it’s still highlighting the entire cell length. Cheers
waleed khalid
AuthorHi
Nice tutorial.
Would like to know if it applies to static grouped cells?
How to do it please.
Thanks
Nick Persinger
AuthorGreat tutorial! However, I have an off-topic question: from what I understand, we have not made these cells do anything yet, am I right? All they do is highlight when they’re tapped. Can we get a tutorial on how to make these cells present information or something? Thank you! 🙂
ilukic
AuthorI agree all these tutorials are very helpful. I have already started working with customizing my own. I am wondering if there is a tutorial about adding multiple views. I would like to expand a similar app as this by adding a main navigation page that has four options example: recipes, tools, substitutions, cooking tips and then each of those categories have there own view. Is there a tutorial to help with this?
Simon Ng
AuthorThis tutorial may be useful for you: http://104.131.120.244/improve-detail-view-controller-storyboard-segue/
gio
AuthorHi…there is a tutorial for pick up the data from sqlite?
Jay Mayu
Authorsearch for core data integration.
Piovezan
AuthorThis line is unnecessary:
cellBackgroundView.image = background;
Joseph Kandi
AuthorIt was great tutorial, but there are some thing you could still do from Storyboard, like changing to clearColor, it will be great to make full use of the properties that can be done with Storyboard and reduce the amount of code, overall, great tutorial, awesome
Thakur
AuthorThank you for the tutorial but how to add detail view when user click on it..
Cedric Buzzboy
AuthorThank you so much for this! How can we add some sections? Help please I’m a n00b!!! Anyway THANK YOU!
Nando
AuthorHello,
wanted to know what object is used in the application ios podcast. It is a table, with a search bar?
Thanks.
nani
Authornuvvu nee vedava shakalu
sonam
AuthorWhat a movie ranjahnaa full boring yaar
Surjit Sdihu
AuthorThanks for great tutorial. I’m Struggling to add horizontal spacing with cells.
please help me. how can i do that ?
J Mayu
AuthorAwesome stuff
Carlos
AuthorGreat tutorial!!!
Just a little advise. Next line is necessary at cellForRowAtIndexPath for a nicer effect, otherwise the cell background is not transparent:
cell.backgroundColor = [UIColor clearColor];
Iris Park
AuthorThe downloadable Xcode project does not work on Xcode 5.1.
Amit Hooda
AuthorWhat was the problem ?
Lucas
AuthorHello!! I’m Facing this problem in XCODE 5 when I first compile.
CustomTableView[3302:70b] -[UITableViewCellContentView setImage:]: unrecognized selector sent to instance 0x9875400
2014-01-28 19:35:12.026 CustomTableView[3302:70b] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[UITableViewCellContentView setImage:]: unrecognized selector sent to instance 0x9875400’
*** First throw call stack:
(
0 CoreFoundation 0x023645e4 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x014be8b6 objc_exception_throw + 44
2 CoreFoundation 0x02401903 -[NSObject(NSObject) doesNotRecognizeSelector:] + 275
3 CoreFoundation 0x0235490b ___forwarding___ + 1019
4 CoreFoundation 0x023544ee _CF_forwarding_prep_0 + 14
5 CustomTableView 0x00003561 -[RecipeViewController tableView:cellForRowAtIndexPath:] + 481
6 UIKit 0x0010e61f -[UITableView _createPreparedCellForGlobalRow:withIndexPath:] + 412
7 UIKit 0x0010e6f3 -[UITableView _createPreparedCellForGlobalRow:] + 69
8 UIKit 0x000f2774 -[UITableView _updateVisibleCellsNow:] + 2378
9 UIKit 0x00105e95 -[UITableView layoutSubviews] + 213
10 UIKit 0x0008a267 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 355
11 libobjc.A.dylib 0x014d081f -[NSObject performSelector:withObject:] + 70
12 QuartzCore 0x02a002ea -[CALayer layoutSublayers] + 148
13 QuartzCore 0x029f40d4 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 380
14 QuartzCore 0x02a00235 -[CALayer layoutIfNeeded] + 160
15 UIKit 0x00145613 -[UIViewController window:setupWithInterfaceOrientation:] + 304
16 UIKit 0x00064177 -[UIWindow _setRotatableClient:toOrientation:updateStatusBar:duration:force:isRotating:] + 5212
17 UIKit 0x00062d16 -[UIWindow _setRotatableClient:toOrientation:updateStatusBar:duration:force:] + 82
18 UIKit 0x00062be8 -[UIWindow _setRotatableViewOrientation:updateStatusBar:duration:force:] + 117
19 UIKit 0x00062c70 -[UIWindow _setRotatableViewOrientation:duration:force:] + 67
20 UIKit 0x00061d0a __57-[UIWindow _updateToInterfaceOrientation:duration:force:]_block_invoke + 120
21 UIKit 0x00061c6c -[UIWindow _updateToInterfaceOrientation:duration:force:] + 400
22 UIKit 0x000629c3 -[UIWindow setAutorotates:forceUpdateInterfaceOrientation:] + 870
23 UIKit 0x00065fb6 -[UIWindow setDelegate:] + 449
24 UIKit 0x00137737 -[UIViewController _tryBecomeRootViewControllerInWindow:] + 180
25 UIKit 0x0005bc1c -[UIWindow addRootViewControllerViewIfPossible] + 609
26 UIKit 0x0005bd97 -[UIWindow _setHidden:forced:] + 312
27 UIKit 0x0005c02d -[UIWindow _orderFrontWithoutMakingKey] + 49
28 UIKit 0x0006689a -[UIWindow makeKeyAndVisible] + 65
29 UIKit 0x00019cd0 -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1851
30 UIKit 0x0001e3a8 -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 824
31 UIKit 0x0003287c -[UIApplication handleEvent:withNewEvent:] + 3447
32 UIKit 0x00032de9 -[UIApplication sendEvent:] + 85
33 UIKit 0x00020025 _UIApplicationHandleEvent + 736
34 GraphicsServices 0x022c22f6 _PurpleEventCallback + 776
35 GraphicsServices 0x022c1e01 PurpleEventCallback + 46
36 CoreFoundation 0x022dfd65 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 53
37 CoreFoundation 0x022dfa9b __CFRunLoopDoSource1 + 523
38 CoreFoundation 0x0230a77c __CFRunLoopRun + 2156
39 CoreFoundation 0x02309ac3 CFRunLoopRunSpecific + 467
40 CoreFoundation 0x023098db CFRunLoopRunInMode + 123
41 UIKit 0x0001dadd -[UIApplication _run] + 840
42 UIKit 0x0001fd3b UIApplicationMain + 1225
43 CustomTableView 0x000021bd main + 141
44 libdyld.dylib 0x0598f70d start + 1
45 ??? 0x00000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)
David Beaudoin
AuthorGreat tutorial! I’ve encountered one really strange issue. It seems like no matter what I do, the x and y offset of the ImageView are never respected; they always render up against the top left corner of the prototype cell. Are you aware of any way to fix this behavior? Have you seen/heard of it before?
Carlos Solares
AuthorJust want to say awesome tutorial.
Justin Domingue
AuthorAwesome tutorial, as always! Extremely simple to follow. Thanks!
Elias Jacobo
AuthorWay is a black line on top some body know? It does not show the battery icon or clock.
Robert L
AuthorAfter some extensive digging I found out this line in the AppDelegate is the culprit:
[[UINavigationBar appearance] setBackgroundImage:navBackgroundImage forBarMetrics:UIBarMetricsDefault];
This page has some good information on why this is: http://www.doubleencore.com/2013/09/developers-guide-to-the-ios-7-status-bar/ namely that “The status bar will inherit the color and transparency of the UINavigationBar”. As our app specifies such a bar, and the background image is not transparent, the status bar inherits this, so it’s there but… invisible.
I have not found a way to have the status bar appear without the navigation bar losing its green color.
smv
AuthorGreat tutorial – thanks!
I’m doing something wrong – only the 5th row is populated with data for me. The remaining 4 rows show up blank. Can you pl help, thanks.
青青河边草
AuthorA great tutorial。。。But it seems that the download link for the sample code(both “empty” and final project code) has been invalid~~~~(>_<)~~~~ . Could someone email to me one copy if you have? I'd appreciate you!O(∩_∩)O~ps:[email protected]
Joey
AuthorHi Simon and everyone! Any way you can send me the Xcode project downloads. The links are not valid…My email is lechat34troisdumans@yahoo dot com. Thank you very much.
Joey
AuthorI would need this file: CustomTableView_Empty.zip and the final version if you have it. Cheers
Joey
AuthorHi Chris, could anyone please send the file that corresponds to the link above referring to this text: “first download the Xcode project here”. My email address is lechat34troisdumans at yahoo dot com. Thanks a lot (I can’t access Dropbox in China…)
Joey
AuthorI meant “Hi everyone!”
Simon Ng
AuthorCould you access github.com in China? I’ll probably put the files there.
Murtaza J Masalawala
AuthorHi Simon, very useful tut and many thanks for doing this. However I have been using this for my app and am at my wits end as to why I get blank rows on once the app loads. I tried using segue and passing the recipe name on that row to the next viewcontroller and that works fine. I have done the 2 things mentioned by Glenn as well i.e. naming the cell the same as the dequeue call and changing the custom heights both on the tableview and the tableviewcell but to no joy ! Any more ideas?
peter
AuthorAnother great tutorial, my only suggestion is to replace:
NSInteger rowCount = [self tableView:[self tableView] numberOfRowsInSection:0];
With:
NSInteger rowCount = [self tableView:[self tableView] numberOfRowsInSection:indexPath.section];
This way it supports tableViews with multiple sections.
Thank you
Jubair O
Authori am a noob… what is Recipe in “cellForRowAtIndexPath” method??