Custom UITableViewCell

Make sure you have JSON Framework installed first.

This is a continuation of More Complex JSON endpoint

The reason why OOP is so powerful is that you can subclass everything to make it work or look just how you want it. The default look of a UITableViewCell is just one line of text. If you want to add some text below it you need to subclass that. So that is what we will do right now.

The first thing we want to do is create a new subclass in your project. You should know how to do this already. File -> New File and select a UITableViewCell subclass. I have named mine ImageCell

Now you can see we have the title in nice big letters and the URL to the image under in in smaller letters and also in grey

I am just going to paste all the code and I have made some comments to show you what I added

First your ImageCell.h should look like

#import 

@interface ImageCell : UITableViewCell {
	// adding the 2 labels we want to show in the cell
	UILabel *titleLabel;
	UILabel *urlLabel;
}

// these are the functions we will create in the .m file

// gets the data from another class
-(void)setData:(NSDictionary *)dict;

// internal function to ease setting up label text
-(UILabel *)newLabelWithPrimaryColor:(UIColor *)primaryColor selectedColor:(UIColor *)selectedColor fontSize:(CGFloat)fontSize bold:(BOOL)bold;

// you should know what this is for by know
@property (nonatomic, retain) UILabel *titleLabel;
@property (nonatomic, retain) UILabel *urlLabel;

@end

There really isn’t any magic going on in the header. So now open up the ImageCell.m file and my contents are as follows.

#import "ImageCell.h"

@implementation ImageCell

// we need to synthesize the two labels
@synthesize titleLabel, urlLabel;

- (id)initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier {
	if (self = [super initWithFrame:frame reuseIdentifier:reuseIdentifier]) {
		// Initialization code

		// we need a view to place our labels on.
		UIView *myContentView = self.contentView;

		/*
		 init the title label.
		 set the text alignment to align on the left
		 add the label to the subview
		 release the memory
		*/
		self.titleLabel = [self newLabelWithPrimaryColor:[UIColor blackColor] selectedColor:[UIColor whiteColor] fontSize:14.0 bold:YES];
		self.titleLabel.textAlignment = UITextAlignmentLeft; // default
		[myContentView addSubview:self.titleLabel];
		[self.titleLabel release];

		/*
		 init the url label. (you will see a difference in the font color and size here!
		 set the text alignment to align on the left
		 add the label to the subview
		 release the memory
		 */
        self.urlLabel = [self newLabelWithPrimaryColor:[UIColor blackColor] selectedColor:[UIColor lightGrayColor] fontSize:10.0 bold:NO];
		self.urlLabel.textAlignment = UITextAlignmentLeft; // default
		[myContentView addSubview:self.urlLabel];
		[self.urlLabel release];
	}

	return self;
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {

	[super setSelected:selected animated:animated];

	// Configure the view for the selected state
}

/*
 this function gets in data from another area in the code
 you can see it takes a NSDictionary object
 it then will set the label text
*/
-(void)setData:(NSDictionary *)dict {
	self.titleLabel.text = [dict objectForKey:@"title"];
	self.urlLabel.text = [dict objectForKey:@"img"];
}

/*
 this function will layout the subviews for the cell
 if the cell is not in editing mode we want to position them
*/
- (void)layoutSubviews {

    [super layoutSubviews];

	// getting the cell size
    CGRect contentRect = self.contentView.bounds;

	// In this example we will never be editing, but this illustrates the appropriate pattern
    if (!self.editing) {

		// get the X pixel spot
        CGFloat boundsX = contentRect.origin.x;
		CGRect frame;

        /*
		 Place the title label.
		 place the label whatever the current X is plus 10 pixels from the left
		 place the label 4 pixels from the top
		 make the label 200 pixels wide
		 make the label 20 pixels high
		*/
		frame = CGRectMake(boundsX + 10, 4, 200, 20);
		self.titleLabel.frame = frame;

		// place the url label
		frame = CGRectMake(boundsX + 10, 28, 200, 14);
		self.urlLabel.frame = frame;
	}
}

/*
 this function was taken from an XML example
 provided by Apple

 I can take no credit in this
*/
- (UILabel *)newLabelWithPrimaryColor:(UIColor *)primaryColor selectedColor:(UIColor *)selectedColor fontSize:(CGFloat)fontSize bold:(BOOL)bold
{
	/*
	 Create and configure a label.
	 */

    UIFont *font;
    if (bold) {
        font = [UIFont boldSystemFontOfSize:fontSize];
    } else {
        font = [UIFont systemFontOfSize:fontSize];
    }

    /*
	 Views are drawn most efficiently when they are opaque and do not have a clear background, so set these defaults.  To show selection properly, however, the views need to be transparent (so that the selection color shows through).  This is handled in setSelected:animated:.
	 */
	UILabel *newLabel = [[UILabel alloc] initWithFrame:CGRectZero];
	newLabel.backgroundColor = [UIColor whiteColor];
	newLabel.opaque = YES;
	newLabel.textColor = primaryColor;
	newLabel.highlightedTextColor = selectedColor;
	newLabel.font = font;

	return newLabel;
}

- (void)dealloc {
	// make sure you free the memory
	[titleLabel dealloc];
	[urlLabel dealloc];
	[super dealloc];
}

@end

So now we can save that file and open up RootViewController.m

We need to add in a import

#import "ImageCell.h"

Now go down to the cellForRowAtIndexPath function. Right now we have a UITableViewCell *cell call to init the cell, we need to change that since we are using our new ImageCell class. So replace that line with

ImageCell *cell = (ImageCell *)[tableView dequeueReusableCellWithIdentifier:MyIdentifier];

Also we need to replace the cell = line in the if (cell == nil) if statement

cell = [[[ImageCell alloc] initWithFrame:CGRectZero reuseIdentifier:MyIdentifier] autorelease];

Now remove the cell.text line and replace it with the following

NSDictionary *itemAtIndex = (NSDictionary *)[self.jsonArray objectAtIndex:indexPath.row];
[cell setData:itemAtIndex];

Now you can build and go and you should get the following

As you can see you now have the title in nice big letters and the URL in smaller gray letters below the title. As always you can grabt he source code here.

About mike
Currently works for Recurly as a Senior Linux Admin. He has a wonderful wife Thanuja and 2 great children (Anusha and Brandon). His major side project is Photoblog.

Comments

50 Responses to “Custom UITableViewCell”
  1. Brian says:

    I’ve been researching how to do this all day! Thanks! BTW, the source code link is broken.

  2. mikezupan says:

    It took me a bit to really figure it out.. I had it hours before I thought I did but I messed up on the cellForRowAtIndexPath function.. I changed the cell init for the first but not if it was nil and my app keep crashing.

    Thanks for pointing out about the source.. the link is fixed now

  3. John says:

    wanted to say thanks.. with the NDA information like this is hard to come by.. esp. the more advanced things like web services and subclassing. Thanks again.

    john

  4. mikezupan says:

    @John

    no problem. I could care less about the NDA.

    I personally think it is there cause Apple changed a lot from the first beta of the SDK to the one there now. I think they didn’t want a lot of bad tutorials out there to discourage people.

    that is just purely a guess though

  5. xavier says:

    Very very usefull entry .. very usefull blog .. I’m definitly learning with you :)

    thanks keep up the good work

  6. zeddy says:

    Very good tutorial :)

    Is there any way of making the cells resize according to the amount of text in the labels?

    I’ve set the labels to have multiple lines, but the part where you set the frame causes problems for dynamic content.

  7. Mark says:

    Was just about to program an application that needed this (I’m a noob) that integrates with my price check site (http://www.dvdpricecheck.co.uk). You example code should work a treat to get me going.

    Thanks mate.

  8. Jeremy says:

    Great posts, have really helped.

    I am trying to extend this example to add an image to each row however so far I seem to be able to make the frame but no picture appears?

    Any Ideas?

  9. mikezupan says:

    @Jeremy

    I was able to add photos just fine to a custom cell. Make sure you do the following

    1) init the image in a UIImage
    2) load the image in a UIImageView
    3) add the UIImageView to the subview which i call it MyContentView
    4) in the layoutSubviews make sure you draw it.

    Sometimes I have forgotten to add the subview and it doesn’t show up..

    If you are still suck post your code somewhere and I can take a look

  10. Jeremy says:

    Hmm maybe my dictionary object is incorrect.

    [menuArray addObject:[NSDictionary dictionaryWithObjectsAndKeys:
    NSLocalizedString(@”Test Title”,@””), @”title”,
    NSLocalizedString(@”Test”,@””), @”description”,
    [UIImage imageNamed:@"test.png"],@”testimg”,
    nil,
    nil]];

    2) self.testImageView = [[UIImageView alloc] initWithImage:[dict objectForKey:@"testimg"]];

    3) [myContentView addSubview:self.testImageView];

    4) frame = CGRectMake(boundsX +5, 4, 40, 40);
    self.testImageView.frame = frame;

    Anything obvious?

  11. mikezupan says:

    Here is how I am doing it.. Now my images are pulled from a webservice so I load them via JSON

    noPicImageSmall = [[UIImage imageNamed:@"nopic-small.png"] retain];

    UIImageView *img1 = [[UIImageView alloc] initWithImage:noPicImageSmall];
    img1.hidden = YES;
    [MyContentView addSubview:img1];
    [imgArray addObject:img1];
    [img1 release];

    might be some issue with how you are loading the image.. also make sure you added it to your Xcode project

  12. Nicolas says:

    Hi Mike,

    Thanks for the great tutorials!

    One question, do you plan writing a tutorial on creating a view from a json resource?

    I have the table with all my items, but I have no idea how to make the links clickable and go to a detailed view of that particular item.

  13. Turbolag says:

    Great tutorial, I’ve been trying to do this for a while.

    However, I would like to edit the size of each cell, so I could fit a 40×40 image in. I have replaced the CGRectZero in newLabelWithPrimaryColor with CGRectMake(8.0, 10.0, 260.0, 80.0), but I dont see any change. How do I increase the size of each cell?

  14. mikezupan says:

    @Turbolag

    the only way I have found to edit the height of a cell is doing it in the tableView.

    -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 100.0; //returns floating point which will be used for a cell row height at specified row index
    }

  15. mikezupan says:

    http://iphone.zcentric.com/2008/08/25/click-a-cell/

    There is a post on how to create a new view from a cell click

  16. Turbolag says:

    @mikezupan
    Thanks, worked great!

    I have added an image to each cell with the following code in setData:

    UIView *myContentView = self.contentView;
    self.image = [UIImage imageNamed: [dict objectForKey:@"image"]];
    UIImageView *imgView = [[UIImageView alloc] initWithImage:image];
    [myContentView addSubview:imgView];

    But this doesn’t follow the method used above where the view is loaded in initWithFrame. When I add the view in initWithFrame, and set the image in setData, the image is not displayed. What code should be placed in initWithFrame?

  17. mikezupan says:

    your self.image line assumes that whatever is in the dictionary for image is already a image resource loaded into a UIImage.

    Here is how I do it for my main app. I have a nopic image that I load by default to show a waiting icon or such then when the real image is loaded it will replace it.

    noPicImageSmall = [[UIImage imageNamed:@"nopic-small.png"] retain];

    So nopic-small.png is in my project

    UIImageView *img1 = [[UIImageView alloc] initWithImage:noPicImageSmall];
    [MyContentView addSubview:img1];
    [img1 release];

    Also make sure you are drawing your image in the layoutSubviews method

  18. Turbolag says:

    So correct me if I’m wrong,

    initWithFrame should contain:
    noPicImageSmall = [[UIImage imageNamed:@"nopic-small.png"] retain];
    UIImageView *img1 = [[UIImageView alloc] initWithImage:noPicImageSmall];
    [MyContentView addSubview:img1];
    [img1 release];

    And setData should contain to update the image for each cell:
    self.image = [UIImage imageNamed: [dict objectForKey:@"image"]];

    What exactly do you mean by drawing image in layoutSubviews meathod? Do you have any source code you can provide?

  19. mikezupan says:

    The next tutorial will be about placing a image in a custom cell

  20. mikezupan says:

    @Turbolag

    Here is a link to adding a UIImageView to a custom cell

    http://iphone.zcentric.com/2008/08/26/uiimageview-in-a-custom-cell/

  21. john says:

    Hi, the sample doesn’t compile (at least for me (it says it needs iphone simulator.sdk) I can compile tons of other sample apps…
    thx

  22. mikezupan says:

    @john,

    thanks the sample code is fixed

  23. Halbert says:

    Awesome post!

    Do you know if its possible to do what you did in code via the Interface Builder?

    Since the code is just subclassing a TableViewCell and adding and positioning subviews to it, it would be easier if you could just do this using an editor, ie the Interface Builder.

    Right now, I’m using the Interface Buildter to visually layout my table cell. When I have it the way I like it, I record all the positions and sizes of each subview element and then write it in code. Very tedious.

  24. mikezupan says:

    @Halbert

    I don’t think you can do that. If you want to do it in IB you can create a new uiview and create a subclass for that and in the cell set the view to load up that xib file probably

  25. samken says:

    Thanks. You are a champ.

  26. billgajen says:

    Hi I am a beginner,

    I don’t understand where I have to replace

    NSDictionary *itemAtIndex = (NSDictionary *)[self.jsonArray objectAtIndex:indexPath.row];
    [cell setData:itemAtIndex];

    I can’t find cell.text line anywhere

    Can you help me. And the sample code is not workoing

    Gajen

  27. mikezupan says:

    there is no cell.text since I am setting the text in the custom cell class

  28. Davide says:

    Thanks mate, this tut is exactly what I was looking for. The framework is well documented, but somehow it is hard to find the information.

  29. Gary says:

    This just saved me about 4 hours of banging my head against the wall. Thanks!

  30. Luis says:

    Ironically, I cannot view your code blocks on my iPhone. They’re cut off on the right. Boo.

  31. BHSPitMonkey says:

    Luis: Use a two-finger swipe to scroll around in the code blocks.

  32. rahulvyas says:

    please tell me how to add multiple buttons programmatically in Custom Cells in diferent row and show new view from that button…?

  33. dineshkumarm12 says:

    I want to have a two cell in a row,as in contacts we have “Text message” and “Add to favourites” in a single row how to do that….
    Is that Splitting a cell into two?

    can anyone help me?

  34. podarki says:

    Вообще, на мой взгляд, самое лучшее в личном блоге, так это самопознание.

  35. Raed says:

    When I trying to run the source code I got this error !!

    Command /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin/gcc-4.2 failed with exit code 1

  36. mum says:

    since u are taking label text from xml,if i have multiple section for each setion i need different label text like…

    section 1

    Animal
    Dog

    section 2
    Flowers
    Rose

    how can i declare differnt uitableviewcell for differntlabel

  37. Eric Goldberg says:

    Just as a heads-up, your UILabel constructor method will leak memory as implemented. You should return an autoreleased UILabel. Or, on the caller side, set the instance property in a way that doesn’t add a retain. However, doing the autorelease way is cleaner and more the “Apple Way” since you’re supposed to balance any init / copy calls with a release / autorelease in the same method.

    Cheers,
    Eric

Trackbacks

Check out what others are saying about this post...
  1. [...] do you just want to display a picture? do you want the picture to be in a list? There’s a tutorial here on setting up custom UITableViewCells, which could be useful if you wanted to display a list of [...]

  2. [...] Custom UITableViewCell | Iphone Noob there is my blog entry on it __________________ Iphone Noob iPhone Development Blog. Learn to develop for the iPhone SDK with me [...]

  3. [...] is mine Custom UITableViewCell | Iphone Noob __________________ Iphone Noob iPhone Development Blog. Learn to develop for the iPhone SDK with [...]

  4. [...] This is a continuation of Custom UITableViewCell [...]

  5. [...] got me going: iPhone Programming Tutorial – Populating UITableView With An NSArray | iCodeBlog Custom UITableViewCell : Iphone Noob Especially the first one, the Fruit-example, is pretty useful. Having model objects as the basis [...]

  6. [...] Custom UITableViewCell : Iphone Noob (tags: iphone development) [...]

  7. [...] Custom UITableViewCell : Iphone Noob. [...]

  8. [...] What this method does is create the label and image required for the cell, adding it to the contentView object of the UITableViewCell parent class. You can read more about subclassing UITableViewCells here. [...]



Speak Your Mind

Tell us what you're thinking...
and oh, if you want a pic to show with your comment, go get a gravatar!

css.php