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.


I’ve been researching how to do this all day! Thanks! BTW, the source code link is broken.
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
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
@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
Very very usefull entry .. very usefull blog .. I’m definitly learning with you
thanks keep up the good work
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.
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.
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?
@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
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?
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
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.
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?
@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
}
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
@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?
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
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?
The next tutorial will be about placing a image in a custom cell
@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/
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
@john,
thanks the sample code is fixed
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.
@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
Thanks. You are a champ.
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
there is no cell.text since I am setting the text in the custom cell class
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.
This just saved me about 4 hours of banging my head against the wall. Thanks!
Ironically, I cannot view your code blocks on my iPhone. They’re cut off on the right. Boo.
Luis: Use a two-finger swipe to scroll around in the code blocks.
please tell me how to add multiple buttons programmatically in Custom Cells in diferent row and show new view from that button…?
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?
vrotmnenogi
Вообще, на мой взгляд, самое лучшее в личном блоге, так это самопознание.
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
thanks
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
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
Good tutorial!
Here is another interesting tutorial! http://www.altinkonline.nl/tutorials/xcode/uitableview/uitableviewcell/