Adding a UITableView as a subview of UITableViewCell using Interface Builder
Posted: June 29, 2011 Filed under: Tutorial | Tags: cell, custom tableview cell, custom uitableviewcell, inside, subview, table, table inside cell, table view, tableview, tableview cell, tableview inside tableviewcell, tableviewcell, tutorial, uitableview, uitableview inside uitableviewcell, uitableviewcell, uitableviewcontroller 34 Comments »Adding a UITableview as a subview of a UITableViewCell??? Yes, you read it correctly and I typed it correctly. No mistakes have been made here. It sounds strange but once the idea came to my mind I had to do try it out to see if it was possible. I searched online and couldn’t find any help so I decided to write a tutorial. Here it is:
Prep work – Getting things ready
First, we shall prepare all the stuff that we would be working with in order to load a UITableView inside a UITableViewCell. Goes without saying but I’d say it anyway, create/open an Xcode project that has a UITableViewController subclass or has a UIViewController subclass with a UITableView in it.
Real Work
Making a custom UITableViewCell
In order to make a custom UITableViewCell, first of all, create a subclass of UITableViewCell, we’ll name it ‘TableViewCell’. In Xcode 4, File > New > New File and in the dialog select UIViewController subclass and in the ‘Next’ dialog type UITableViewCell in the ‘subclass’ text field.
Step 1: Adjust Objects and Properties in Xib File
Open the xib file of your UITabelViewCell subclass and incase you cannot change the size of the view, delete that view, add a new UIView, and adjust the height to the value you need. I made it 200 so that I can have eight 25 pi long cells inside this cell (25×8=200).
Next thing, change the class of the ‘View’ object. Select the ‘View’ object, open ‘Identity Inspector’, and type your subclass’s name i.e. ‘TableViewCell’.
After that change the ‘File’s Owner’ to the UITableViewController subclass (or the class that has the table view whose cell this is)
Next create the IBOutlet for the UITableView inside this cell in the header file for UITableViewCell’s subclass, let’s name it tableViewInsideCell. In XIB, connect the IBOutlet and set the delegate and dataSource of the tableViewInsideCell to the ‘View’ object (whose class we just changed to ‘TableViewCell’) not the File’s Owner. This is an important step, so be careful. We will only make one connection to the File’s Owner and that is going to be with the ‘View’ object of this xib.
(See how the dataSource, delegate and ref outlet connections are made with the ‘TableViewCell’ rather than the File’s Owner.)
Step 2: Implement the UITableView Delegate and DataSource in UITableViewCell subclass
In the header, add the protocol that this class is implementing, i.e. UITableView delegate and data source. So, TableViewCell.h would look like:
#import <UIKit/UIKit.h>
@interface TableViewCell : UITableViewCell <UITableViewDelegate, UITableViewDataSource> {
NSDictionary *data;
}
@property (nonatomic, retain) IBOutlet UITableView *tableViewInsideCell;
@property (nonatomic, retain) NSDictionary *data;
@end
Note that there is an NSDictionary object in .h file. This will be used to transfer data from the UITableViewController subclass to the UITableViewCell subclass. Use this dictionary in a switch or an appropriate conditional statement to set appropriate data values for each cell inside the tableView:cellForRowAtIndexPath delegate method.
TableViewCell.m would have the UITableView delegate and dataSource methods. Here is what it would look like:
#import "TableViewCell.h"
@implementation TableViewCell
@synthesize tableViewInsideCell;
@synthesize data;
- (void)dealloc {
[data release];
[tableViewInsideCell release];
[super dealloc];
}
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Custom initialization
}
return self;
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return 8;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1
reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell...
switch (indexPath.row) {
case 0:
cell.textLabel.text = @"Latitude";
cell.detailTextLabel.text = [data objectForKey:@"Latitude"];
break;
case 1:
cell.textLabel.text = @"Longitude";
cell.detailTextLabel.text = [data objectForKey:@"Longitude"];
break;
case 2:
cell.textLabel.text = @"Speed";
cell.detailTextLabel.text = [data objectForKey:@"Speed"];
break;
case 3:
cell.textLabel.text = @"Altitude";
cell.detailTextLabel.text = [data objectForKey:@"Altitude"];
break;
case 4:
cell.textLabel.text = @"Date";
cell.detailTextLabel.text = [data objectForKey:@"Date"];
break;
case 5:
cell.textLabel.text = @"Time";
cell.detailTextLabel.text = [data objectForKey:@"Time"];
break;
case 6:
cell.textLabel.text = @"Course";
cell.detailTextLabel.text = [data objectForKey:@"Course"];
break;
case 7:
cell.textLabel.text = @"Accuracy";
cell.detailTextLabel.text = [data objectForKey:@"Accuracy"];
break;
default:
break;
}
return cell;
}
#pragma mark - Table view delegate
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 25;
}
@end
Adding the custom UITableViewCell to the Tableview
In order to add this custom cell to our tableview, the first step is to make an outlet for ‘TableViewCell’ (your UITableViewCell’s subclass) inside your UITableViewController subclass. This is what the ‘TableViewController.h’ would look like:
#import <UIKit/UIKit.h>
@class TableViewCell;
@interface TableViewController : UITableViewController {
NSArray *dataArray;
}
@property (nonatomic, retain) IBOutlet TableViewCell *tableViewCellWithTableView;
@end
Open TableViewCell.xib and make the ‘View’ object’s (whose class we changed to ‘TableViewCell’) connection with the File’s Owner. We are doing this because we changed the File’s Owner class to our UITableViewController subclass. So our cell’s outlet in the tableview controller header will be connected to the custom cell that we created
Making the connections is confusing but its following a simple rule; connections are made with the class that has the outlet declared in it. Here is the list of all connections made in TableViewCell.xib:
1. Custom cell’s View <-> File’s Owner | class: TableViewController (because we need the cell object in TableViewController class to assign in tableView:cellForRowAtIndexPath)
2. Tableview inside the cell <-> custom cell’s View | class: TableViewCell (because the tableView is inside the cell)
3. UITableViewDelegate & UITableViewDataSource <-> custom cell’s View | class: TableViewCell (because the tableview that is inside the cell has its delegate and data source implemented in our custom cell’s class. These two connections are very important, if these are made with the File’s Owner by mistake then remember the File’s Owner which is tableview controller is implementing its own delegate and data source for the top most tableview, so you wouldn’t get an error but also not any output)
Anyway, coming back. After making the connection in the xib, add the custom TableViewCell to the table view like so:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = @"CellWithTableView";
TableViewCell *cell = (TableViewCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:@"TableViewCell" owner:self options:nil];
tableViewCellWithTableView.data = [dataArray objectAtIndex:indexPath.row];
tableViewCellWithTableView.tableViewInsideCell.allowsSelection = NO;
cell = tableViewCellWithTableView;
[cell setNeedsDisplay];
}
return cell;
}
Note that the cell needs to be loaded from the xib rather than by using the initWithStyle:reuseIdentifier method. Also note that the NSDictionary object called ‘data’ that was declared in the custom cell header is being assigned the appropriate element from the dataArray. The dataArray was initialized to have NSDictionary objects, like so:
NSDictionary *one = [[[NSDictionary alloc] initWithObjectsAndKeys:@"13.861970", @"Latitude",
@"100.504250", @"Longitude",
@"50 kph", @"Speed",
@"1000 meters", @"Altitude",
@"12/04/2010", @"Date",
@"05:45 PM", @"Time",
@"North west", @"Course",
@"+/- 10 meters", @"Accuracy", nil] autorelease];
NSDictionary *two = [[[NSDictionary alloc] initWithObjectsAndKeys:@"13.862470", @"Latitude",
@"100.501389", @"Longitude",
@"60 kph", @"Speed",
@"1050 meters", @"Altitude",
@"12/04/2010", @"Date",
@"06:15 PM", @"Time",
@"North", @"Course",
@"+/- 10 meters", @"Accuracy", nil] autorelease];
NSDictionary *three = [[[NSDictionary alloc] initWithObjectsAndKeys:@"13.861970", @"Latitude",
@"100.504250", @"Longitude",
@"70 kph", @"Speed",
@"1000 meters", @"Altitude",
@"12/04/2010", @"Date",
@"06:35 PM", @"Time",
@"South", @"Course",
@"+/- 10 meters", @"Accuracy", nil] autorelease];
NSDictionary *four = [[[NSDictionary alloc] initWithObjectsAndKeys:@"13.861970", @"Latitude",
@"100.504250", @"Longitude",
@"50 kph", @"Speed",
@"1050 meters", @"Altitude",
@"12/04/2010", @"Date",
@"06:55 PM", @"Time",
@"South West", @"Course",
@"+/- 10 meters", @"Accuracy", nil] autorelease];
dataArray = [[NSArray alloc] initWithObjects:one, two, three, four, nil];
That is all, just remember to set the cell height to the height of your custom cell.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 200;
}
With the above dummy data, my table looked like this:
This is the most raw form and can be customized in many ways to look nicer. I hope someone out there finds it useful…
Trying this and actually succeeding in it made me realize that iOS SDK is flexible and any UIView subclass element to any other UIView subclass. So don’t be afraid to implement new ideas no matter how wild they might appear. For those eager to try wild stuff, have a look at ‘Pulse’, its a news reader app that won Apple’s Design Award 2011. This app has horizontally scrollable images and its developers explained that they have used UITableViews to create that. I might try that myself and post a tutorial ![]()
Update: Pulse-style scrolling tutorial has been published. Enjoy!
Update
Borut pointed out that our custom cells with UITableView inside them are not being reused. If we study the code carefully, we will find out that we are not assigning any reuseIdentifier because we are not using the default -initWithStyle:reuseIdentifier method for creating cells. The reuseIdentifier property of UITableViewCell is readonly , we cannot set it. A workaround is to create a getter method in our UITableViewCell subclass for the reuseIdentifier and use the same string being used to dequeue the reusable cells. In our case, this should do:
- (NSString *) reuseIdentifier {
return @"Cell";
}
Note that in this tutorial we are using @”Cell” for dequeuing cells in both UITableViewCell subclass and UITableViewController subclass. This needs to return the one we are using in the UITableViewCOntroller subclass because we want to reuse this cell there.
Update 2
Thanks a lot for your appreciation. Since there were a lot of requests for code. I’ve uploaded it to dropbox. Here is the link:
http://dl.dropbox.com/u/17653793/TableViewInsideCell.zip





[...] Adding a UITableView as a subview of UITableViewCell using Interface Builder [...]
Hi. I’ll see if I can, I mean I’m still new to wordpress and last time I tried, it didn’t let me upload a zip file. And I’m using Xcode 4.0
Thanks a lot for your quick reply. Would you be able to email it to me – techchick2010@gmail.com?
Sure.
Hi,
Thanks for the tutorial.
Could you please upload the tutorial project to the blog post? Some of the steps are difficult to follow. Also are you using XCode 4.0 or XCode 3.2?
You can post the project in github and share the github link. Hope this helps.
Thanks
Hi, amazing job!!! Where can I find the project? Thanks a lot
Hi, thanks. I just emailed you
Open TableViewCell.xib and make the ‘View’ object’s (whose class we changed to ‘TableViewCell’) connection with the File’s Owner. We are doing this because we changed the File’s Owner class to our UITableViewController subclass. So our cell’s outlet in the tableview controller header will be connected to the custom cell that we created
Can you please break down these steps into further small steps. I am having really hard time connecting IBOUTLET tableViewCellWithTableView to TableViewCell.
Is it possible to mail the project for reference?
Thanks!
Ill further break it down:
1. Open TableViewCell.xib
2. Click on ‘View’ and Open Identity inspector
3. Change the class name to TableViewCell, previously it must be UIView.
4. Click on File’s Owner and open identity inspector again.
5. Change the class name to TableViewController, previously it must be TableViewCell
6. Now open the ‘Connections inspector’ on file’s owner.
7. You must see tableViewCellWithTableView outlet not connected to anything. Connect this to the ‘View’
I hope this helps you. Ill email you the project for reference
Thanks a lot! I was able to make it out after few hours and carefully reading your post again.
Thanks for the code as well. I will try to do it while reading the post first (Going to the pulse one now). In case I get stuck, will refer the code.
Hi,
Thanks very much for the tutorial, very interesting. Unfortunately, I get a little lost between the setup in the prep work and Step 1.
I take it you opened a View-based project with the name TableViewInsideCell. I can see in the first image that you have the App Delegate with this name but I don’t see the corresponding View Controller that should have been created. Instead you have TableViewController. Did you simply rename the files?
Sorry, still a little new and trying to understand.
Yup, used Refactor > Rename
just to make things clear
Thanks.
I got stuck towards the end. “After making the connection in the xib, add the custom TableViewCell to the table view like so: …” This replaces the previous method in TableViewCell or goes into TableViewController? I take it the latter but I’m getting some errors …
Would you mind sharing the project?
Thanks,
Norman
This goes in TableViewController. I have emailed you the project. Happy Coding
Hi there
Thanks very much for posting this! Fun stuff to play with.
Would you mind emailing me the project aswel?
Cheers
Not at all. Im doing that rite now
Thank you!
Here are a few places I got stuck (may be obvious to some but I’m pretty new), hopefully this helps anyone else working through this.
Step 2:
NSString *cellData;
should be
NSDictionary *data;
The dataArray gets initialised in viewDidLoad
TableViewController.m needs
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [dataArray count];
}
Keep up the iOS posts!
Oooops….thanks for pointing that out. Ive changed it now
Ok…i’m completely lost right on the creating IBOutlet. Mind to send me the sample project please, thank you.
Oh btw, thank you for posting this tut…i’ve been looking for this sometimes now.
Great job.
I am a bit new for iphone developing and got some error in this sample. mainly in cellForRowAtIndexPath.
could you please sending me the project.
thanks
Can the user actually select a cell from the inner table using this approach?
I tried another approach, but the inner cells are not selectable. didSelect never gets called for the inner UITableView allthough dataSource and delegate are set correctly and the table is looking nice.
Yes peter. The inner row is selectable. Make sure you set tableView.allowsSelection to YES. In this code ive set it to NO
Great tutorial. Just what I needed. You think you can send me the project?
Mailed u. Have fun
Thanks a lot! I was able to make it out after few hours and carefully reading your post again.
Nice tutorial, i am stuck in between. Would you be able to email the project to me. Thanks
You can download the project from the link given at the end of the post. Which is:
http://dl.dropbox.com/u/17653793/TableViewInsideCell.zip
Thanks ….. its nice….
Awesome post, that’s what i’m looking for!
Great Stuff!
Just went through it myself in XCode 4.3.2 and I noticed that there’s a little change in the first step you say:
“In Xcode 4, File > New > New File and in the dialog select UIViewController subclass and in the ‘Next’ dialog type UITableViewCell in the ‘subclass’ text field”
Thing is that XCode doesn’t let you select the ‘UIViewController’ subclass anymore. In stead you create .h and .m file as a subclass for the UITableViewCell, after which you need to create a view to get .xib file.
Being new to XCode and iOS, it took me a while to figure this out, but learnt a lot from this.
Thanks for sharing!
Hi, how can I reply this example but with storyboard?
I’m curious about storyboarding, too. I’ve got the download working, but the xib architecture is notably different from how the tables would appear in a storyboard in Xcode 4.5. Have you updated this at all on your own?
I’m planning on taking a shot at this on my own. If I get it working, then I’ll let you know.
Nice job with the concept, by the way. I tried this on my own and have had all sorts of issues. I like the thinking behind your implementation.