Three20 TTTableItem Tutorial
Say Hello to Three20 TTTableItemCells
Over the last couple of months I’ve shown you how to use Joe Hewitt’s Three20 iPhone library to create css-like stylesheets for your iPhone Apps, as well as custom table cells for your iPhone Apps.
But a while ago Joe changed up the way we make custom table cells. Instead of using TTTableField and TTTableFieldCell we now use TTTableItem and TTTableItemCell to accomplish custom table cells.
So today I am going to show you how to create an iPhone App using Three20’s TTTableItem and TTTableItemCell.
Enough talk! Lets start the show
This is the game plan for creating our custom table cells:
- First we’re going to decide on, and then design the custom cell we want for our App
- Then we’re going to setup our App, create some necessary files, etc
- And we’re going to make our own TableItems by subclassing TTTableItem
- We’re going to make our own TableItemCells by subclassing TTTableItemCell
- We’re going instantiate our new TableItems in our DataSource
- We’re going to make the App render the correct TableItemCell for each TableItem we just made
- And then finally when we’ve done all the other stuff ^, we’ll have a sexy new app with sexy custom table cells
1. Designing our Custom Cell
Due to several questions and requests about putting images in custom cells, I think I’ll show you how to put some images, as well as text into our cell
Our App will look something like this:

2. Setup the App
I think the easiest way to learn this shiznit, is to follow along step by step with this tutorial (i’ve created a three20 App template that works great for this)
- Download or Clone my Three20 Tutorial repository from github
- When my repo is downloaded or cloned open it and copy and past the basic-navigation-app-template somewhere else on your computer (if you want)
The three20-iPhone-tutorials folder should now look like this:

- Rename basic-navigation-app-template copy folder to something like tableitem-tutorial-working
- Open the Xcode Project file inside of your new folder (again we’re using a template App that I created)
- Now we need to create some new classes inside of XCode:
- We need a new class that will subclass TTTableItem. Go File >> New File >> Objective-C Class >> Name it BNTableItem (following Joe’s naming conventions). Make sure “Also create BNTableItem.h” is checked.
- We also need a new class where we will sublclass TTTableItemCell. Go File >> New File >> Objective-C Class >> Name it BNTableItemCell (following Joe’s naming conventions). Make sure “Also create BNTableItemCell.h” is checked.
- Now that our all of our files are created let’s give our App a title. Go into RootViewController.m and change the line
self.title = @”Tutorial Title”;
to
self.title = @”TableItem Tutorial”;.
RootViewController.m should now look like this:
#import "RootViewController.h" #pragma mark import dataSource #import "RootViewDataSource.h" # pragma mark import table cells @implementation RootViewController /////////////////////////////////////////////////////////////////////////////////////////////////// // UIViewController - (void)loadView { [super loadView]; self.tableView = [[[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped] autorelease]; self.tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; self.variableHeightRows = YES; self.title = @"TableItem Tutorial"; [self.view addSubview:self.tableView]; } ////////////////////////////////////////////////////////////////////////////////////////////////// // TTTableViewController - (id)createDataSource { return [RootViewDataSource rootViewDataSource]; } @end - Make sure that you have selected Device 3.0 or Simulator 3.0 to run the app
- Open the App and see what happens. Click Build and Go (or Build >> Build and Go (Run)).
The App should look like this (nothing special yet, but just you wait):
3. Subclass TTTableItem
Now we need to make our TableItem. To do this we are going to subclass TTTableCaptionedItem.
TableItems hold the data, TableItemCells actually display the data.
Sink that into your head, it’s important:
TableItems = Holds the data, TableItemCells = Displays the data held in it’s TableItem
Do the following steps, once they are done I will explain what was accomplished.
- Go into BNTableItem.h and DELETE EVERYTHING
- Go into BNTableItem.m and DELETE EVERYTHING
- We should now have NOTHING in either BNTableItem.h or BNTableItem.m
- Now we need to subclass TTTableCaptionedItem because it’s quite similar to what we are trying to accomplish, but needs some enhancements. Copy and paste this code into the empty BNTableItem.h file:
#import "Three20/Three20.h" /////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////// ////// BNTableCaptionedItemWithThreeImagesBelow ///// //////////////////////////////////////////////////////// @interface BNTableCaptionedItemWithThreeImagesBelow : TTTableCaptionedItem { } @end - Now copy and paste this code into BNTableItem.m:
#import "BNTableItem.h" /////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////// ////// BNTableCaptionedItemWithThreeImagesBelow ///// //////////////////////////////////////////////////////// @implementation BNTableCaptionedItemWithThreeImagesBelow @end
We just created a subclass of TTTableCaptionedItem which we named BNTableCaptionedItemWithThreeImagesBelow.
But we haven’t changed anything in our class yet, let’s do that now:
- Go back to BNTableItem.h and modify it so the file looks exactly like this:
#import "Three20/Three20.h" /////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////// ////// BNTableCaptionedItemWithThreeImagesBelow ///// //////////////////////////////////////////////////////// @interface BNTableCaptionedItemWithThreeImagesBelow : TTTableCaptionedItem { NSString* _image1; NSString* _image2; NSString* _image3; TTStyle* _imageStyle; } @property(nonatomic,copy) NSString* image1; @property(nonatomic,copy) NSString* image2; @property(nonatomic,copy) NSString* image3; @property(nonatomic,retain) TTStyle* imageStyle; + (id)itemWithText:(NSString*)text caption:(NSString*)caption image1:(NSString*)image1 image2:(NSString*)image2 image3:(NSString*)image3; @end - Go to back to BNTableItem.m and modify it so the file looks exactly like this:
#import "BNTableItem.h" /////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////// ////// BNTableCaptionedItemWithThreeImagesBelow ///// //////////////////////////////////////////////////////// @implementation BNTableCaptionedItemWithThreeImagesBelow @synthesize image1 = _image1, image2 = _image2, image3 = _image3, imageStyle = _imageStyle; /////////////////////////////////////////////////////////////////////////////////////////////////// // class public + (id)itemWithText:(NSString*)text caption:(NSString*)caption image1:(NSString*)image1 image2:(NSString*)image2 image3:(NSString*)image3 { BNTableCaptionedItemWithThreeImagesBelow* item = [[[self alloc] init] autorelease]; item.text = text; item.caption = caption; item.image1 = image1; item.image2 = image2; item.image3 = image3; return item; } /////////////////////////////////////////////////////////////////////////////////////////////////// // NSObject - (id)init { if (self = [super init]) { _image1 = nil; _image2 = nil; _image3 = nil; _imageStyle = nil; } return self; } - (void)dealloc { TT_RELEASE_MEMBER(_image1); TT_RELEASE_MEMBER(_image2); TT_RELEASE_MEMBER(_image3); TT_RELEASE_MEMBER(_imageStyle); [super dealloc]; } /////////////////////////////////////////////////////////////////////////////////////////////////// // NSCoding - (id)initWithCoder:(NSCoder*)decoder { if (self = [super initWithCoder:decoder]) { self.image1 = [decoder decodeObjectForKey:@"image1"]; self.image2 = [decoder decodeObjectForKey:@"image2"]; self.image3 = [decoder decodeObjectForKey:@"image3"]; } return self; } - (void)encodeWithCoder:(NSCoder*)encoder { [super encodeWithCoder:encoder]; if (self.image1) { [encoder encodeObject:self.image1 forKey:@"image1"]; } if (self.image2) { [encoder encodeObject:self.image2 forKey:@"image2"]; } if (self.image3) { [encoder encodeObject:self.image3 forKey:@"image3"]; } } @end
Finished that? Good. I am not going to explain everything that we did there, but I will give an overview.
- We added 3 new properties to our class (one for each image)
- We made a method called + (id)itemWithText:(NSString*)text caption:(NSString*)caption image1:(NSString*)image1 image2:(NSString*)image2 image3:) which accepts the addresses for each of the 3 images as well as our text content
- We added the 3 image properties to the init method, the initWithCoder method, and encodeWithCoder method.
The BNTableItem part of this tutorial is now finished.
The next part of our tutorial deals with displaying the data from our TableItem (the labels, images, etc), lets get started
4. Subclass TTTableItemCell
Subclassing TTTableCaptionedItemCell (itself a subclass of TTTableItemCell) will be similar to the subclassing we just did of TTTableCaptionedItem.
- Go into BNTableItemCell.h and DELETE EVERYTHING
- Go into BNTableItemCell.m and DELETE EVERYTHING
- We should now have NOTHING in either BNTableItemCell.h or BNTableItemCell.m
- Now subclass TTTableCaptionedItemCell. Go into and BNTableItemCell.h and make it look like this:
#import "Three20/Three20.h" /////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// ////// BNTableCaptionedItemWithThreeImagesBelowCell // //////////////////////////////////////////////////////////// @interface BNTableCaptionedItemWithThreeImagesBelowCell : TTTableCaptionedItemCell { TTImageView* _imageView1; TTImageView* _imageView2; TTImageView* _imageView3; } @end - Then go into BNTableItemCell.m and make it look like this:
#import "BNTableItemCell.h" #import "BNTableItem.h" #import "BNDefaultStylesheet.h" /////////////////////////////////////////////////////////////////////////////////////////////////// static CGFloat kHPadding = 10; static CGFloat kVPadding = 15; static CGFloat kImageWidth = 80; static CGFloat kImageHeight = 80; /////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////// ////// BNTableCaptionedItemWithThreeImagesBelowCell //// ////////////////////////////////////////////////////////////// @implementation BNTableCaptionedItemWithThreeImagesBelowCell + (CGFloat)tableView:(UITableView*)tableView rowHeightForItem:(id)item { BNTableCaptionedItemWithThreeImagesBelow* captionedItem = item; CGFloat maxWidth = tableView.width - kHPadding*2; CGSize textSize = [captionedItem.text sizeWithFont:TTSTYLEVAR(myHeadingFont) constrainedToSize:CGSizeMake(maxWidth, CGFLOAT_MAX) lineBreakMode:UILineBreakModeWordWrap]; CGSize subtextSize = [captionedItem.caption sizeWithFont:TTSTYLEVAR(mySubtextFont) constrainedToSize:CGSizeMake(maxWidth, CGFLOAT_MAX) lineBreakMode:UILineBreakModeWordWrap]; return kVPadding*2 + textSize.height + subtextSize.height + kImageHeight + kVPadding; } /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)identifier { if (self = [super initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:identifier]) { _item = nil; _imageView1 = [[TTImageView alloc] initWithFrame:CGRectZero]; [self.contentView addSubview:_imageView1]; _imageView2 = [[TTImageView alloc] initWithFrame:CGRectZero]; [self.contentView addSubview:_imageView2]; _imageView3 = [[TTImageView alloc] initWithFrame:CGRectZero]; [self.contentView addSubview:_imageView3]; } return self; } - (void)dealloc { TT_RELEASE_MEMBER(_imageView1); TT_RELEASE_MEMBER(_imageView2); TT_RELEASE_MEMBER(_imageView3); [super dealloc]; } /////////////////////////////////////////////////////////////////////////////////////////////////// // UIView - (void)layoutSubviews { [super layoutSubviews]; [self.detailTextLabel sizeToFit]; self.detailTextLabel.top = kVPadding; self.textLabel.height = self.detailTextLabel.height; _imageView1.frame = CGRectMake(20, self.detailTextLabel.bottom + kVPadding, kImageWidth, kImageHeight); _imageView2.frame = CGRectMake(_imageView1.right + kHPadding, self.detailTextLabel.bottom + kVPadding, kImageWidth, kImageHeight); _imageView3.frame = CGRectMake(_imageView2.right + kHPadding, self.detailTextLabel.bottom + kVPadding, kImageWidth, kImageHeight); } /////////////////////////////////////////////////////////////////////////////////////////////////// // TTTableViewCell - (id)object { return _item; } - (void)setObject:(id)object { if (_item != object) { [super setObject:object]; BNTableCaptionedItemWithThreeImagesBelow* item = object; self.textLabel.textColor = TTSTYLEVAR(myHeadingColor); self.textLabel.font = TTSTYLEVAR(myHeadingFont); self.textLabel.textAlignment = UITextAlignmentRight; self.textLabel.contentMode = UIViewContentModeCenter; self.textLabel.lineBreakMode = UILineBreakModeWordWrap; self.textLabel.numberOfLines = 0; self.detailTextLabel.textColor = TTSTYLEVAR(mySubtextColor); self.detailTextLabel.font = TTSTYLEVAR(mySubtextFont); self.detailTextLabel.textAlignment = UITextAlignmentLeft; self.detailTextLabel.contentMode = UIViewContentModeTop; self.detailTextLabel.lineBreakMode = UILineBreakModeWordWrap; _imageView1.URL = item.image1; _imageView1.style = item.imageStyle; _imageView2.URL = item.image2; _imageView2.style = item.imageStyle; _imageView3.URL = item.image3; _imageView3.style = item.imageStyle; } } @end
Woooo we’re on a roll, we just subclassed TTTableCaptionedItemCell, then modified it so that it has 3 image views underneath the heading and caption.
- In the rowHeightForItem method we made our cell adjust it’s size to the text content AND our new images.
- In the initWithStyle method we allocated 3 TTImageViews (to hold our images) and added the TTImageViews as subviews of our main view.
- in the layoutSubviews method we made the textLabel (the heading label) and the detailTextLabel (the subtext label) fit together nicely, as well as sizing all three images and making them fit nice and centered underneath the text.
- Finally in the setObject method we set the textColor, the font, and a few other important properties of our textLabel, and detailTextLabel, and we gave each of our 3 TTImageViews the URL of the image their supposed to display (the URL of each TTImageView should match the corresponding URL given for each address we are fed to our TTTableItem).
Now we need to put our feed our TableItems to our TableView datasource
5. Add Our TableItems to the DataSource
Now what we’re going to do is instantiate and feed BNTableCaptionedItemWithThreeImagesBelow into our DataSource a few times while feeding it some text content and image addresses.
-
To instantiate and feed our Tableitem into our datasource (twice), make RootViewDataSource.m look exactly like this:
#import "RootViewDataSource.h" #import "BNTableItem.h" #import "BNTableItemCell.h" @implementation RootViewDataSource /////////////////////////////////////////////////////////////////////////////////////////////////// // public + (RootViewDataSource*)rootViewDataSource { RootViewDataSource* dataSource = [[[RootViewDataSource alloc] initWithItems: [NSMutableArray arrayWithObjects: [BNTableCaptionedItemWithThreeImagesBelow itemWithText:@"These are some pictures of me (Matt) doing different stuff" caption:@"Matt" image1:@"http://mattvague.com/wordpress/wp-content/uploads/2009/08/boating.jpg" image2:@"http://mattvague.com/wordpress/wp-content/uploads/2009/08/cooking.jpg" image3:@"http://mattvague.com/wordpress/wp-content/uploads/2009/08/surfing.jpg"], [BNTableCaptionedItemWithThreeImagesBelow itemWithText:@"These are some pictures of Vancouver, BC" caption:@"VanCity" image1:@"http://mattvague.com/wordpress/wp-content/uploads/2009/08/Vancouver_Aerial.jpg" image2:@"http://mattvague.com/wordpress/wp-content/uploads/2009/08/Coast-Mountains-BC.jpg" image3:@"http://mattvague.com/wordpress/wp-content/uploads/2009/08/Vancouver_Aerial_2.jpg"], nil]] autorelease]; return dataSource; } /////////////////////////////////////////////////////////////////////////////////////////////////// - (void)dealloc { [super dealloc]; } /////////////////////////////////////////////////////////////////////////////////////////////////// // TTTableViewDataSource - (Class)tableView:(UITableView*)tableView cellClassForObject:(id) object { if ([object isKindOfClass:[BNTableCaptionedItemWithThreeImagesBelow class]]) { return [BNTableCaptionedItemWithThreeImagesBelowCell class]; } else { return [super tableView:tableView cellClassForObject:object]; } } - (void)tableView:(UITableView*)tableView prepareCell:(UITableViewCell*)cell forRowAtIndexPath:(NSIndexPath*)indexPath { cell.accessoryType = UITableViewCellAccessoryNone; } @end -
Now try launching the App again.
Looks kinda weird doesn’t it? That’s because we need to do one last thing which is to tell our App which kind of TableItemCell to render for our TableItems.
Right now the App is just rendering the closest matching TableItemCell it could find, but not the one that we want (which is the one we just created).
6. Render the correct TableItemCell for our TableItems
This part is very simple, and I probably could have combined it with the last step, but it is something that is quite vital and I think deserves its own step.
- Add the following code to RootViewDataSource.m, underneath the rootViewDataSource, and dealloc methods:
/////////////////////////////////////////////////////////////////////////////////////////////////// // TTTableViewDataSource - (Class)tableView:(UITableView*)tableView cellClassForObject:(id) object { if ([object isKindOfClass:[BNTableCaptionedItemWithThreeImagesBelow class]]) { return [BNTableCaptionedItemWithThreeImagesBelowCell class]; } else { return [super tableView:tableView cellClassForObject:object]; } } - Now RootViewDataSource.m should look exactly like this:
#import "RootViewDataSource.h" #import "BNTableItem.h" #import "BNTableItemCell.h" @implementation RootViewDataSource /////////////////////////////////////////////////////////////////////////////////////////////////// // public + (RootViewDataSource*)rootViewDataSource { RootViewDataSource* dataSource = [[[RootViewDataSource alloc] initWithItems: [NSMutableArray arrayWithObjects: [[BNTableCaptionedItemWithThreeImagesBelow itemWithText:@"These are some pictures of me (Matt) doing different stuff" caption:@"Matt" image1:@"http://mattvague.com/wordpress/wp-content/uploads/2009/08/boating.jpg" image2:@"http://mattvague.com/wordpress/wp-content/uploads/2009/08/cooking.jpg" image3:@"http://mattvague.com/wordpress/wp-content/uploads/2009/08/surfing.jpg"] autorelease], [[BNTableCaptionedItemWithThreeImagesBelow itemWithText:@"These are some pictures of Vancouver, BC" caption:@"VanCity" image1:@"http://mattvague.com/wordpress/wp-content/uploads/2009/08/Vancouver_Aerial.jpg" image2:@"http://mattvague.com/wordpress/wp-content/uploads/2009/08/Coast-Mountains-BC.jpg" image3:@"http://mattvague.com/wordpress/wp-content/uploads/2009/08/Vancouver_Aerial_2.jpg"] autorelease] ,nil]] autorelease]; return dataSource; } /////////////////////////////////////////////////////////////////////////////////////////////////// - (void)dealloc { [super dealloc]; } /////////////////////////////////////////////////////////////////////////////////////////////////// // TTTableViewDataSource - (Class)tableView:(UITableView*)tableView cellClassForObject:(id) object { if ([object isKindOfClass:[BNTableCaptionedItemWithThreeImagesBelow class]]) { return [BNTableCaptionedItemWithThreeImagesBelowCell class]; } else { return [super tableView:tableView cellClassForObject:object]; } } - (void)tableView:(UITableView*)tableView prepareCell:(UITableViewCell*)cell forRowAtIndexPath:(NSIndexPath*)indexPath { cell.accessoryType = UITableViewCellAccessoryNone; } @end
Done yet? Hope so, cause that’s the last step!
7. Run the App
Run our App by clicking on “Build and Go”
It should look like this:
Wrap Up / Download the working version
If you are still having trouble or just want to checkout the source code, the final working version of my tutorial is availbile on github as part of my three20 tutorial repository. Download or clone my repo and then open the tableitem-tutorial folder and open its Xcode Project.
If you have any questions please ask. The best way is to leave a comment below.
Thanks!
Comments on this Post
Questions? Comments? Kindly-worded insults?
Thats right, it's all welcome folks! Step right up and leave a your mark for the ages!


August 27, 2009 - Brian Knittel
Looks like a great tutorial, but doesn’t work with the source as of Aug 26.
August 27, 2009 - MattV
Sorry your having trouble with my tutorial, could you possibly be a bit more specific as to what is not working or what error comes up?
-Matt
August 27, 2009 - Mirco
I get 25 errors, 9 warnings:
/Users/indy0130/Desktop/mattvague-three20-iPhone-tutorials-c279d07d892a7963c30bc451f0f86e9608792de8/tableitem-tutorial/Classes/BNTableItemCell.m:37: error: syntax error before ‘UITableViewCellStyle’
/Users/indy0130/Desktop/mattvague-three20-iPhone-tutorials-c279d07d892a7963c30bc451f0f86e9608792de8/tableitem-tutorial/Classes/BNTableItemCell.m:41: error: initializer element is not constant
/Users/indy0130/Desktop/mattvague-three20-iPhone-tutorials-c279d07d892a7963c30bc451f0f86e9608792de8/tableitem-tutorial/Classes/BNTableItemCell.m:42: error: syntax error before ‘[’ token
/Users/indy0130/Desktop/mattvague-three20-iPhone-tutorials-c279d07d892a7963c30bc451f0f86e9608792de8/tableitem-tutorial/Classes/BNTableItemCell.m:44: error: initializer element is not constant
and so on
August 27, 2009 - MattV
I really feel bad about the trouble you guys are having, but If you don’t mind helping me solve these issues that would be awesome.
What happens when you run the tableitem-tutorial app? Does it run?
August 27, 2009 - MattV
Make sure your running the App in 3.0!!! I just realized the App didn’t default to that, but now it does.
August 28, 2009 - Three20 TTTableItem Tutorial | Matt’s Portfolio | designoMatt
[...] Three20 TTTableItem Tutorial | Matt’s Portfolio Filed in newsPost in 08282009, 9:19 / Modified at 08282009, 9:19 via mattvague.com [...]
August 30, 2009 - Simon
works perfectly. definetly hepled me grasp t20 table cell styles in an easier fashion than decrypting joe’s headers.
September 1, 2009 - nonoDream
It almost worked.. But the Images can’t show…jst 6 Black areas….
September 1, 2009 - MattV
hmm weird… are you sure your connected to the internet, cause it grabs them from my server.
-Matt
September 7, 2009 - john
Thanks for the tutorial.
One quick question, How to use your “basic-navigation-app-template.” as a base and add “PhotoTest2Controller” or photo gallery one to it.
September 11, 2009 - MattV
John,
To be honest I have yet to toy around with the PhotoGallery features of three20, so how about this: give me a couple of days and I will look into using my app in a manner similar to PhotoTest2Controller. Ill report back what I find as soon as I can.
-MattV
September 17, 2009 - Maaz
Thanks Matt for the great tutorial.
Currently I am confused in 2 things.
1. If we have multiple cells that load images from server during scroll the old image comes in cell until the new image is loaded. This issue is resolved if creating multiple identifiers.
2. How to place a button in the cell which can call the controller?
September 18, 2009 - Alex
Hello Matt
The app get Crash if we scroll the cell such that one cell hides.
September 18, 2009 - MattV
I am aware of this issue and I am working on it, thanks!
-MattV
September 26, 2009 - Fred
Hello Matt,
I got the same problem as Alex did. The app crashes as I scroll the table view.
Fred
September 26, 2009 - fred
Hi Matt,
I found out the cause to the crash.
In the method (RootViewDataSource*)rootViewDataSource,
Don’t autorelease the array item BNTableCaptionedItemWithThreeImagesBelow.
Fred
September 26, 2009 - MattV
Fred,
You are 100% correct, that was the problem! I’m not sure how that got overlooked :-S
Anways I just updated the app and pushed the new version to github so it should be fixed now.
Thanks a lot man!
-MattV
September 30, 2009 - KLee
Hi Matt,
How would you make some of the text in the custom cell as links (buttons?) so that it changes to a new view when tapped? For example, “Vancouver, BC” would push a new view that shows information about the city.
Thanks!
October 9, 2009 - Matt
It took me a few minutes to figure this one out… The newest build of Three20 calls the rowHeight method a little differently from TTTableViewDelegate
- (CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath {
id dataSource = (id)tableView.dataSource;
id object = [dataSource tableView:tableView objectForRowAtIndexPath:indexPath];
Class cls = [dataSource tableView:tableView cellClassForObject:object];
return [cls tableView:tableView rowHeightForObject:object];
}
Notice that it now says rowHeightForObject, not rowHeightForItem.
Just rename the subclassed method and it will be the right height again
October 12, 2009 - Felipe Navarro V.
Thank you very much for sharing this… has been really helpful!
October 18, 2009 - lol
“cell’s”?
Dude, seriously, go back to elementary school.
October 20, 2009 - MattV
Wud u happen to be a elementry skool grammer teachur?? Cause I does needs some grammer lessons
-MattV
October 25, 2009 - Luis
So, Matt, I saw your update on the TTDefaultStyleSheet saying it was deprecated. So what’s the fix? You link to this tutorial from that one.
Note that I just built my first simple test app with Three20. It only has a TTTabItem, and one UILabel.
What does the stylesheet style? I need some background. That would help as well.
Also, re grammar nazi – you also say it’s in your other blog post…. who cares really, lol…. this is the interwebz.
October 25, 2009 - MattV
Luis,
My old TTStylesheets tutorial is deprecated because it uses TTTableFields to create TableViewCells (which is the old way of doing it). The new way is to use TTTableItems, which is what this tutorial goes over.
To answer your question “what’s the fix?”, I guess the easiest thing to do would be to kind of merge the still-applicable parts of my TTStylesheets tutorial (everything up to step 4) with what you learn from doing THIS tutorial.
As far as I know the only part of my TTStylesheets tutorial that is deprecated is step 4. That’s the part where you actually apply the styles to the UILabels inside of a TTTableFieldCell (like _label.textColor = TTSTYLEVAR(myFirstColor);). The rest of the tutorial should work still work as long as you know how to use TTTableItems, and can adapt them for step 4 (rather then TTTableFields).
Another way to learn how TTStylesheets work would be to check out the finished app for this tutorial (availible on github: http://github.com/mattvague/three20-iPhone-tutorials). The finished app uses TTStylesheets to style the font and color of it’s TTTableItemCells, but unfortunatly the scope of this tutorial does not cover how to use the stylesheets like my other tutorial.
So again, I think your two options would be:
1. Learn how TTTableItems work with THIS tutorial, and then learn how TTStylesheets work with the OTHER tutorial, and then combine what you’ve learned from both tutorials.
or
2. Clone (download) my tutorial repository and take a look at the source code. You could probably get a good idea of how TTStylesheets work.
Good luck, tell me if you have any more questions or problems!
October 29, 2009 - Caleb
The cellClassForObject snippet of this tutorial just solved a major headache for me. I’m sure you know what it was
Thank you!
October 29, 2009 - Chris
hello , thanks a lot for your great work ..
can you please post the sample project of this tutorial ..
October 29, 2009 - MattV
Chris,
Sorry if it wasn’t clear in my tutorial, but the final working version of the tutorial app is available on github in my three20 Tutorial Repository: http://github.com/mattvague/three20-iPhone-tutorials
-MattV
November 3, 2009 - Toto
Hi !
Shouldn’t “Displays the data held in it’s TableItem” be “in its TableItem”..?
November 3, 2009 - Yasir
hello matt i need to display the products in table view and when i click the table view cell it should navigate to the zoomed scroll view i created a TTTableImageItemCell and add some labels to it but i couldn’t get “DidSelectObject” method , its doing nothing ..
Can you please tell me how can i call the that method to navigate to next view and also tell me how to display the selected picture in scrollview…
Thanks..
November 3, 2009 - MattV
Yasir,
Instead of using the DidSelectObject method for navigating views in your app, I instead suggest using three20’s TTNavigator. Included with three20 is a demo application called TTNavigatorDemo which should get you up and running pretty quick.
But if you insist on using DidSelectObject, here is some sample code from an old project of mine (not sure if it’s still compatible with the current version of three20):
- (void)didSelectObject:(id)object atIndexPath:(NSIndexPath*)indexPath {
if ([object class] == [MyExampleTableItem class]) {
MyExampleTableItem *myField = object;
MyViewController *myViewController = [[MyViewController alloc] init];
myViewController.product = myField.product;
[self.navigationController pushViewController:myViewController animated:YES];
[myViewController release];
} else if ([object class] == [MyOtherExampleTableItem class]) {
MyOtherExampleTableItem *myOtherField = object;
MyOtherViewController *myOtherViewController = [[MyOtherViewController alloc] init];
myOtherViewController.product = myOtherField.product;
[self.navigationController pushViewController:myOtherViewController animated:YES];
[myOtherViewController release];
}
}
Hope that helps!
-MattV
November 3, 2009 - Yasir
Thanks a lot Matt ,You gave me a great example code.but can i use ttnavigator ? because i want the exact functionality as in image grid view of three 20 library , when i click on any image it automatically navigate to zoomed image of that small thumbnail ,TTthumbsviewcontroller to ttphotoviewcontroller…
but i dont have a datasource in my tableview to push the viewcontroller to ttphotoviewcontroller . i am stucked here ttphotviewcontroller needs id and i don’t know how to create this object i have image url only , i also tried scrollview but in that view how can i set the selected image because i am providing an array or images to scroll view to display ..
can you please clear one more thing , i have confusion that if i use three20 library and i want to submit application in apple store. will it create a problem in submission ? i mean i don’t know three 20 is using private or undocumented api …
Matt Your website is very helpfull for iphone developers ..speacially for those who want to use three 20 library .
thanks again .
November 3, 2009 - Yasir
sorry i missed a word in above post ..2nd paragraph and 2nd line
its like this ..
i am stucked here ttphotviewcontroller needs TTphoto and i don’t know how to create this object i have image url only
November 5, 2009 - nickbit
Very nice tutorial!
Is it possible to mix URL images (http://…) with local images or ABContactBook
images??
How can this be done?
November 9, 2009 - MattV
nickbit,
I’m sure its doable, you’ll probably have to make a bundle of images like in TTCatalog. Look in TTCatalog and the Three20 source code and im sure you’ll get a good idea of how to do that.
-MattV
November 13, 2009 - PaulF
I thought that the file naming convention is to have the file name agree with the name of the class being declared and implemented. It’s a bit of a surprise that you don’t follow this in your tutorial. For example, BNTableItem.h suggests that the main class defined in this file will be BNTableItem, not BNTableCaptionedItemWithThreeImagesBelow. [Note that I'm not talking about any file naming convention which the author of Three20 might have suggested (not sure what that is, really). Instead, it's a generally agreed upon convention of Objective C coders that I'm referring to here. Hewitt follows this convention, as far as I can tell from his examples.]
November 13, 2009 - admin
Paul,
The reason I name the file BNTableItem rather than the class name, is because this file is intended to contain ALL TableItems for this App (if I were to have more than one). Check out TTTableItem.h/.m in the three20 source code, Joe does the same thing.
-MattV
November 17, 2009 - ermes
hallo there a class named TTTableCaptionedItem ? I got an error that
” error: cannot find interface declaration for ‘TTTableCaptionedItem’, superclass of ‘BNTableCaptionedItemWithThreeImagesBelow” I use the same project “basic navigation app template”
What I do wrong?
November 17, 2009 - Phil Freo
What is “TTTableCaptionedItem” ? A Google search for the class only brings up this article?
November 17, 2009 - Phil
Ah, looks like it should be called “TTTableCaptionItem” from:
http://github.com/facebook/three20/blob/8183ae25528bbc575ab41a41227756f06c166240/src/Three20/TTTableItem.h
If you update your tutorial and files to the latest version, that would be greatly appreciated
November 19, 2009 - pablasso
Besides what Phil mentioned, please note that “TT_RELEASE_MEMBER” was renamed to “TT_RELEASE_SAFELY”
November 19, 2009 - MattV
Yes I apologize, this tutorial was based on an older version of three20 and a few things have changed since then.
When I have some time, I plan to update this tutorial but until then I’m sure ya’ll can work around it
January 8, 2010 - Adam Tong
Hi Matt, great tutorial, ive been coding in objective-c and xcode for about 2months now, having never done c++ or OOP programming before and im just wondering, where is it stated that the text and caption should be nested next to each other.
I.e. i would like to make the caption be on one line all the way across the top, with the text below it and then the images, not the caption and text side by side.
January 13, 2010 - Adam
I managed to work it out myself.
I replaced the layoutSubviews bit with some new code and it worked!
After 10-12 hours of solid researching into this i’ve attempted to subclass a tableview myself, and have managed to do everything correct (i think), except i am not sure what i am meant to add into BNTableItemCell.m for the implementation of my new class when im adding a label, as aposed to an image.
January 15, 2010 - Brian Elliott
As mentioned previously by another person, this looks great but I am concerned about the use of using private APIs that could cause my iPhone app to get rejected when submitted to the App Store. I saw some verbiage stating that someone was working to remedy this problem but it is not clear to me whether the code has been updated to accomplish this yet? Thanks!
January 26, 2010 - rod yancy
Are private APIs still being used? Also, is anyone have problems running this on the phone. I can get the project to run beautifully in the simulator, but it crashes on the phone.
February 14, 2010 - fabian
I changed TTTableCaptionedItem to TTTableCaptionItem and TTTableCaptionedItemCell to TTTableCaptionItemCell
Now i got this error:
ld: duplicate symbol .objc_class_name_BlogTableItemCell in /Users/username/Development/Workspaces/iphone_experiments/xcode_build_output/MausLog.build/Debug-iphonesimulator/MausLog.build/Objects-normal/i386/BlogTableItemCell-18F77E6981C37B70.o and /Users/username/Development/Workspaces/iphone_experiments/xcode_build_output/MausLog.build/Debug-iphonesimulator/MausLog.build/Objects-normal/i386/BlogTableItemCell-18F77E6981C37B70.o
February 14, 2010 - fabian
Can s.o please update the example for the latest three20 release. i really get a lot of errors i am not able to fix.
thanks!
March 11, 2010 - Getting The Most Out Of The Three20 Open Source Library | iPhone and iPad SDK Development Tutorials and Programming Tips
[...] Three20 Stylesheets – enables you to CSS like stylesheets within your apps. Three20 TTTableItemTutorial – Make very cool custom tableview cells. [...]