Using the default UITableViewCell we can display a text or an image but sometimes it is not good enough. In this tutorial you will learn how to customize the UITableViewCell.

Introduction
Using the default UITableViewCell, we can display a text (using the text property) or an image (using the image property). However, we cannot display data in multiple columns or rows. This is the sixth tutorial in the UITableView tutorial series and borrows its source code from the previous one. This is how the final app will look like

We cannot achieve the display we want by setting the text property of the UITableViewCell. To solve this problem, we are going to add two labels to the content view of the cell. The labels are added in such a way that they show up one above the other by using coordinates. Apple documentation says we should add subviews to the cell’s content view, if you do not want to change the default behavior of the cell. This is the reason why we are not going to inherit from UITableViewCell.


Adding Sub Views
Instead of creating a cell by using initWithFrame:reuseIdentifier, we will create it from our own custom method. This method will be responsible for not only creating the cell but also the labels. The labels are created with specific locations in the cell using x and y coordinates. They are added to the content view of the cell as seen below

//RootViewController.h
#import <UIKit/UIKit.h>
 
@class OverlayViewController;
 
@interface RootViewController : UITableViewController {
 
NSMutableArray *listOfItems;
NSMutableArray *copyListOfItems;
IBOutlet UISearchBar *searchBar;
BOOL searching;
BOOL letUserSelectRow;
 
OverlayViewController *ovController;
}
 
- (UITableViewCell *) getCellContentView:(NSString *)cellIdentifier;
- (void) searchTableView;
- (void) doneSearching_Clicked:(id)sender;
 
@end
 
//RootViewController.m
- (UITableViewCell *) getCellContentView:(NSString *)cellIdentifier {
 
CGRect CellFrame = CGRectMake(0, 0, 300, 60);
CGRect Label1Frame = CGRectMake(10, 10, 290, 25);
CGRect Label2Frame = CGRectMake(10, 33, 290, 25);
UILabel *lblTemp;
 
UITableViewCell *cell = [[[UITableViewCell alloc] initWithFrame:CellFrame reuseIdentifier:cellIdentifier] autorelease];
 
//Initialize Label with tag 1.
lblTemp = [[UILabel alloc] initWithFrame:Label1Frame];
lblTemp.tag = 1;
[cell.contentView addSubview:lblTemp];
[lblTemp release];
 
//Initialize Label with tag 2.
lblTemp = [[UILabel alloc] initWithFrame:Label2Frame];
lblTemp.tag = 2;
lblTemp.font = [UIFont boldSystemFontOfSize:12];
lblTemp.textColor = [UIColor lightGrayColor];
[cell.contentView addSubview:lblTemp];
[lblTemp release];
 
return cell;
}

The above method creates three rectangles: one for the cell and for the two labels in the cell. We create the cell using initWithFrame:reuseIdentifier, but this time we add two labels to the content view property. Initialize the first label with “Label1Frame” coordinates, which will make this label show up at the top of the cell. Set the tag of this label to 1, so we can find it in tableView:cellForRowAtIndexPath method. Initialize the second label using “Label2″Frame” coordinates, which will make this label to show up at the bottom. The tag of this label is set to 2. The labels are added to the content view using addSubView method. Finally the cell is returned to be reused.

Displaying data

Once we have the table view cell, we can get the underlying label by passing viewWithTag message to the cell. Here the data is set to the text property of the label and not the cell. This is how the code for tableView:cellForRowAtIndexPath changes

//RootViewController.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
 
static NSString *CellIdentifier = @"Cell";
 
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
 
if(cell == nil)
cell = [self getCellContentView:CellIdentifier];
 
UILabel *lblTemp1 = (UILabel *)[cell viewWithTag:1];
UILabel *lblTemp2 = (UILabel *)[cell viewWithTag:2];
 
if(searching) {
 
lblTemp1.text = [copyListOfItems objectAtIndex:indexPath.row];
lblTemp2.text = @"";
}
else {
 
//First get the dictionary object
NSDictionary *dictionary = [listOfItems objectAtIndex:indexPath.section];
NSArray *array = [dictionary objectForKey:@"Countries"];
NSString *cellValue = [array objectAtIndex:indexPath.row];
 
lblTemp1.text = cellValue;
lblTemp2.text = @"Sub Value";
 
[cellValue release];
}
 
return cell;
}

If you run this application now you will notice some obvious display issues, which we can resolve by specifying the height of the row. This is done in tableView:heightForRowAtIndexPath method. The code looks like this

//RootViewController.m
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
 
return 60;
}

All the rows in the table view will have a height of 60. Run your app to see the changes.


Conclusion
In this tutorial we saw how easy it is to customize the table view cell. The same principle can be applied to customize the display by adding different controls to the content view. Instead of adding labels a progress bar can also be added. I hope this tutorial has helped you in some way.

Happy Programming,
iPhone SDK Articles


Attachments

Suggested Readings

 

18 Responses to UITableView – Adding subviews to a cell’s content view

  1. praveen says:

    Hi,

    Can you post a sample where table will be a subview of another table like accordion menu. I need that sample code for accordion menu.

    thanks in advacne
    praveen

  2. Thx for this great tutorial serie.

    Some things have change with new SDK version (there are TableViewCell Styles, AccessoryType moved as a property of TableViewCell, etc) but it make a good start, anyway.

    Furthermore, I think it’s a good thing not to have all pre-made and have to think by yourself.

    I hope the Anonymous from march 11th 2009 got his answer but it may help someone else so I’m giving you an hint (yes, just an hint because as I said above, giving pre-made answers is not the best way to learn). So the hint is “CellIdentifier” ;-)

  3. sathish says:

    >Great tutorial….

    Very very nice…. i am novice to iPhone SDK and it helped me a lot…..

    thanks…

  4. CarBoyCam says:

    >How would I go about changing the Sub label text depending on the Cell? e.g. Not having the same on each cell.

    Thanks

  5. Anonymous says:

    >Hi,

    Thank you for creating this blog.

    I’ve enjoyed the tutorials you’ve posted so far, and I hope you keep the series going.

    Here’s a question about customizing this example to maintaining the subview in landscape mode.

    I changed these lines in the getCellContentView method (inPortraitOrientation is an instance BOOL with keeps track of the state of the display orientation):

    CGRect CellFrame = (inPortraitOrientation) ? CGRectMake(0, 0, 300, 60) :
    CGRectMake(0, 0, 460, 60);
    CGRect Label1Frame = (inPortraitOrientation) ? CGRectMake(10, 10, 290, 25)
    : CGRectMake(10, 10, 450, 25);
    CGRect Label2Frame = (inPortraitOrientation) ? CGRectMake(10, 33, 290, 25)
    : CGRectMake(10, 10, 450, 25);

    They’re correct, in the sense that if inPortraitOrientation is NO, the subview fills the entire space available.

    But, it seems that once the view loads, the getCellContentView method is never called again.

    I tried calling:

    [self.tableView reloadData];

    every time the orientation changes, but the getCellContentView method never gets called again.

    How can I invoke getCellContentView after the display changes from portrait to landscape?

  6. Drudoo says:

    >Hey
    Nice tutorial :)
    One question. Is it possible to have a Tab Bar with (5-10 Tabs) and then add this UITableview (with all the features; subview, searching, etc) to every Tab?
    And how do you edit the detailview??? I wanna add a picture and some text to every detailview, but cant find out were it is -.-

    Hope someone can help :D

  7. iPhone SDK Articles says:

    >@Bjorn
    I am sorry I do not understand your question. Do you want to create a cell within a cell?

    Please feel free to send me a detailed email here iphonearticles [@] gmaildotcom and I will get back to you.

    Happy Programming,
    iPhone SDK Articles

  8. Anonymous says:

    >Hi, thanks for creating this blog, and I hope you keep posting tutorials; I’ve enjoyed the series very much so far.

    For this particular example, how can I get the custom frame to resize itself when the iphone changes orientation from portrait to landscape, and vice versa?

    I thought I had the problem solved with these changes to the getCellContentView method:

    CGRect CellFrame = (inPortraitOrientation) ? CGRectMake(0, 0, 300, 60) : CGRectMake(0, 0, 460, 60);
    CGRect Label1Frame = (inPortraitOrientation) ? CGRectMake(10, 10, 290, 25) : CGRectMake(10, 10, 450, 25);
    CGRect Label2Frame = (inPortraitOrientation) ? CGRectMake(10, 33, 290, 25) : CGRectMake(10, 10, 450, 25);

    but it seems getCellContentView is called just once, at initialization.

    How can I get the method getCellContentView to be invoked whenever the orientation changes?

  9. Björn says:

    >Hi!

    Can I use this method for creating an outline with table cells within table cells, or is that your next tutorial?

    /BE

  10. Anonymous says:

    >Thanks
    1. Yes, it happens with the code downloaded from this tutorial. I can scroll down to New Zealand, but when I lift my finger (or rather let go of mouse button in simulator) the list bounces so the search bar is at the top again. So, in effect, searching for 'land' only lets me select the first two results – Iceland and Greenland. I'm using OS 2.1.

    I guess 2 & 3 would be solved by putting the table on a View, and putting to search box on the same view, separate from the table.

    4. That makes sense. I was trying to understand Apple's TableSearch sample code before looking at your code, so I was expecting it to work a little differently.

    Thanks
    SINDY

  11. iPhone SDK Articles says:

    >@SINDY

    1. I did a search by using the keyword “land” and I was able to scroll down to select “New Zealand”. What you say, does it happen in the code attached with this tutorial?
    2. I suppose the search bar can be fixed above the table view. Let me find a neat way to do this.
    3. If you look at the source code found in this tutorial http://www.iphonesdkarticles.com/2009/01/uitableview-searching-table-view.html, the search bar is of the same height as in this one.
    4. The search results is cleared to keep the user experience consistent with the way searching works in the contacts app. If the search results is not cleared then we would require another button/click to refresh the original data.

    I hope this helps, please let me know if I can be of any more assistance.

    Happy Programming,
    iPhone SDK Articles

  12. Anonymous says:

    >Thanks for all these great UITableView tutorials!

    I have a few issues with the functionality of the last three of your Table View tutorials (the ones involving search). I was wondering if these issues could be solved by adjusting your code, or whether a totally different approach is needed.

    1. When you enter ‘land’ into the search box, you can’t scroll down to select ‘New Zealand’ because the table ‘bounces’ back up to the top. Can this ‘bounce’ be turned off?
    2. Can the search bar remain fixed in position, with the countries list scrolling underneath it if necessary?
    3. When each cell has two lines of text, The height of the search bar mirrors the height of the other table cells (too deep). Can the search bar remain at a standard depth?
    4. When clicking ‘done’ , can the entered text remain, so entering ‘land’ and then pressing done will display only the countries with ‘land’ in their name. Likewise, pressing Search on the keyboard would do the same thing.

    Sorry for all the questions, and thanks again!
    SINDY

  13. iPhone SDK Articles says:

    >@Simone I am afraid I do not understand your question. I do have a tutorial showing how to display a detail view here http://www.iphonesdkarticles.com/2009/01/uitableview-loading-detail-view.html.

    Do you want to add controls to the view using Interface Builder? If yes, then you can do it by adding the control to the nib file and referencing it from Xcode. In the case of this tutorial, you will add the control to “DetailView” nib file and reference it from DetailViewController.

    Let me know if this helps.

    Happy Programming,
    iPhone SDK Articles

  14. iPhone SDK Articles says:

    >@Anonymous I will be posting a tutorial showing how to do that soon.

    Happy Programming,
    iPhone SDK Articles

  15. Anonymous says:

    >Can you post a sample where table will be a subview of another table?

  16. iPhone SDK Articles says:

    >I have been experiencing some problems with Google sites where I host all my source code. You should be able to download it now, if you cannot please send me an email and I will send you the source code.

    Sorry about the inconvenience.

    Happy Programming,
    iPhone SDK Articles

  17. Anonymous says:

    >I get an error when trying to download the source file…

    We are sorry, but this site has exceeded its bandwidth limit at this time. Please try again later. For more information, see Google Sites help.

  18. Simone says:

    >This is cool. Thank you for sharing.
    But what if I want to load my subview from a Nib file and not build the view manually inside the controller?
    Is it possibile?
    There is a initWithNibFile method for the UIViewController, but not for a generic UIView.
    Is there another approach to loading it?
    Simone