Creating Unique Looking Tables with Custom Cells

A reader of this post How to Add “More Content” Indicators asked about how the user interface was constructed for the Bikini.com application, the UI in question is shown below:

Creating a Custom Table

It’s quite simple to pull this together, it’s nothing more than a UITableView that is transparent so the background shows through. Each cell is a UITableViewCell which allows for customization of the cell.

Before we walk through the code, what follows is a screenshot of the example we’ll create:

The class definition for the main viewcontroller, which holds the tableview, looks as follows:

@interface MainViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
{
  UITableView *theTableView;
}

Notice that I am not using a UITableViewController, rather a UIViewController with a UITableView. Also, note the reference to UITableViewDelegate and UITableViewDataSource.

Let’s look at how I initialize the viewcontroller:

- (id)init
{
  if (self = [super init]) 
  {
    self.view = [[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease];
 
    // Setup the background
    UIImageView *background = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"background.png"]];
    [self.view addSubview:background];
    [background release];
 
    // Create table view
    theTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 11, 320, 460) style: UITableViewStylePlain];
    [theTableView setDelegate:self];
    [theTableView setDataSource:self];
 
    // This should be set to work with the image height
    [theTableView setRowHeight:68];
 
    // Transparent, so we can see the background
    [theTableView setBackgroundColor:[UIColor clearColor]];
    [theTableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];
    [theTableView setIndicatorStyle:UIScrollViewIndicatorStyleWhite];
 
    [self.view addSubview:theTableView];
 
  }
  return self;
}

I create the view, add a background image and define a tableview. I set the table row size to correlate with the image size I want to show and mark the table background as transparent (clearColor).

The other chunk of relevant code is where we supply cells as the table is constructed. Here’s how that looks:

1
2
3
4
5
6
7
8
9
10
11
12
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{
  CustomCell *cell= [[[CustomCell alloc] initWithFrame:CGRectZero reuseIdentifier:nil] autorelease];
 
  // Default to no selected style and not selected
  cell.selectionStyle = UITableViewCellSelectionStyleNone;
 
  // Set the image for the cell  
  [cell setTheImage:[UIImage imageNamed:[NSString stringWithFormat:@"Arrows%d.png", indexPath.row + 1]]];
 
  return cell;
}

Notice that rather than a UITableViewCell I am referring to the CustomCell class which I create below. The other point of interest is line 13 where I set the image to display in the cell (read on for more about this).

Creating a Custom Cell

Let’s look at the custom cell code, again, no rocket science, it’s nothing more than UIImageView and a method for setting the image in the cell. Depending on what you need in the user interface, you may also include a label or other UI component in this class.

#import <UIKit/UIKit.h>
 
@interface CustomCell : UITableViewCell 
{
  UIImageView *image; 
}
 
- (void) setTheImage:(UIImage *)icon;
 
@end

There are just three methods inside the implementation of CustomCell: initialization, setting the image for the cell and setting the alpha of the selected row:

#import "CustomCell.h"
 
@implementation CustomCell
 
/*---------------------------------------------------------------------------
* 
*--------------------------------------------------------------------------*/
- (id)initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier 
{
  if (self = [super initWithFrame:frame reuseIdentifier:reuseIdentifier]) 
  {
    // Cells are transparent
    [self.contentView setBackgroundColor:[UIColor clearColor]];
  }
  return self;
}
 
/*---------------------------------------------------------------------------
* 
*--------------------------------------------------------------------------*/
- (void) setTheImage:(UIImage *) icon
{  
  // Alloc and set the frame
  image = [[UIImageView alloc] initWithImage:icon];
  image.frame = CGRectMake(0, 0, 286, 68);
 
  // Add subview
  [self.contentView addSubview:image];    
}
 
/*---------------------------------------------------------------------------
*
*--------------------------------------------------------------------------*/
- (void)setSelected:(BOOL)selected animated:(BOOL)animated 
{
  [super setSelected:selected animated:animated];   
  if (selected == YES)
    image.alpha = .5;
  else
    image.alpha = 1;
}
 
/*---------------------------------------------------------------------------
* 
*--------------------------------------------------------------------------*/
- (void)dealloc 
{
  [image release];
  [super dealloc];
}
 
@end

That’s all there is to creating a basic custom tableview.

Download Xcode Project

If you’d like to give this project a try, you can download the source code here.

  1. Hi, I love the technique… but I have a problem. I downloaded your source, built in 3.0 for simulator and device, and when it first loads it works fine, all the cells are selectable (they darken when selected). But immediately after scrolling to the bottom (row 8) and back up to the top, I can no longer select rows 1, 2, or 7, 8. All the others are selectable, and clicking 1,2,7,8 will deselect the selected one, but no longer do these rows highlight (darken). Bug, or a non-intuitive feature? I’ll start looking at the code next to see if I’m missing something. -Bitfool

    • I’ve found that with caching of the table cells, this can be a problem. I updated the code above and the Xcode project to remove the cell caching (in cellForRowAtIndexPath method) and it appears to work fine.

      Honestly, I’m not sure if that’s a bug in the implementation or a side-effect that one has to deal with when using custom cells.

  2. Fantastic: thanks very much for that code. I’m a Win32/C++ programmer trying to figure all this stuff out for the first time and I got lost in UITableViewControllers that don’t allow for background images.

    I’m also trying to do stuff without the InterfaceBuilder (learning how everything plugs together in code and all that). The only catch I ended up with after my modifications is that my table cell clicked on code does fire, but the view I’m pushing on doesn’t appear any longer. I then discovered that none of the viewLoaded() events are getting fired any longer in my root view controller. I guess I did too much at once: removed all the NIB files and added this :) The table’s great, by the way, just that I’ve lost the firing of all the view controller overloaded functions. Any clues as to what schoolboy error I’m making? I’m sure it’s trivial.

    • Well first, glad to hear from are working without IB, as I personally find that the best means to learn how things work (I’m not much for black boxes). After a year, I’m still flying solo without IB as I’ve become accustomed to doing everything from the ground up. With that said, I’m sure if I took the time to learn IB, I would find great value in the tool for certain aspects of development.

      As far as viewLoaded not being called, are the classes inherited from UIViewController?

  3. I am, of course, a complete doofus. Needless to say that a brief espresso break followed with a nice glass of white illuminated my beginner’s stupidity. My mistake was thus:

    I was doing this in my app delegate:

    m_pMainViewController = [[RootViewController alloc] init];

    As opposed to this:

    m_pMainViewController = [[UINavigationController alloc] initWithRootViewController:[[RootViewController alloc] init]];

    … and then everything started working nicely. Which is nice! As for your other comments; I’m glad it’s not just me. I tried the Interface Builder a little but it seemed to detach me from what was going on in the code and more often than not, I dragged one thing to where it shouldn’t be, etc. The problem that I am having is that many of the books and web sites are focused on doing this stuff with IB so it’s a slow haul whilst I figure out basic errors like the above.

    Thanks again for your article, btw., it was more than helpful: I’m finally beginning to understand how all this stuff fits together. If I can just stop typing std::vector, -> and virtual ~ViewController() all the time and actually fall into Objective C, I’m laughing. :-)

  4. Hi,

    I would like to know how it can be achieved if the images and content of table view cell is to be downloaded over the air and refreshed for every specific time.

    Any info would be appreciable.

  5. Hi,

    How can you achieve the glass like look and feel?

Comments are closed.