← Back

Three20 TTTableItem Tutorial

Aug 22, 2009 - Posted in Three20, Tutorials, iPhone

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:

  1. First we’re going to decide on, and then design the custom cell we want for our App
  2. Then we’re going to setup our App, create some necessary files, etc
  3. And we’re going to make our own TableItems by subclassing TTTableItem
  4. We’re going to make our own TableItemCells by subclassing TTTableItemCell
  5. We’re going instantiate our new TableItems in our DataSource
  6. We’re going to make the App render the correct TableItemCell for each TableItem we just made
  7. 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:

Picture 3

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)

  1. Download or Clone my Three20 Tutorial repository from github
  2. 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:

    Picture 14

  3. Rename basic-navigation-app-template copy folder to something like tableitem-tutorial-working
  4. Open the Xcode Project file inside of your new folder (again we’re using a template App that I created)
  5. Now we need to create some new classes inside of XCode:
    1. 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.
    2. 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.
  6. 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  
  7. Make sure that you have selected Device 3.0 or Simulator 3.0 to run the app
  8. 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):

    Picture 1

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.

  1. Go into BNTableItem.h and DELETE EVERYTHING
  2. Go into BNTableItem.m and DELETE EVERYTHING
  3. We should now have NOTHING in either BNTableItem.h or BNTableItem.m
  4. 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  
  5. 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:

  1. 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  
  2. 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.

  1. Go into BNTableItemCell.h and DELETE EVERYTHING
  2. Go into BNTableItemCell.m and DELETE EVERYTHING
  3. We should now have NOTHING in either BNTableItemCell.h or BNTableItemCell.m
  4. 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
  5. 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.

  1. 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
  2. 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.

  1. 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];
    	}
    }
  2. 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:

Picture 4

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

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. [...]

April 21, 2010 - Joe

If you use the latest version of Three20, you shouldn’t have any private API issues. Jeff Verkoeyen has been very conscientious about keeping it that way. Use at will!

August 26, 2010 - Paul S

Guys, I updated to the latest three20 and I’m getting the same error as fabian. There are a lot of compilation issues even after fixing the Captioned to Caption. Updates and fixes will be appreciated. This should be a pretty common task, no? so hopefully someone with a solution will not be too difficult to find. sorry for being a three20 noob!

Paul

August 26, 2010 - Paul S

After lots of cleaning and rebooting, using a different Xcode, and so on, I decided to remove the class names like “..WithThreeImages..” and just use the files’ names which let me get past the symbol error.

So ok I got to the point where I am editing the layoutSubviews code that Adam was probably working on, with the latest three20. Thought I’d post my able-to-be-compiled code with the latest three20 lib for anyone struggling with this. The following are some notes of changes made.

* In your TableViewController
- (id)createDataSource is apparently not called in the new three20?
Add in your loadView or somewhere similar
self.dataSource = [TableViewDataSource tableViewDataSource];

* In your TableItemCell subclass, change your layout subviews like this
- (void)layoutSubviews {
[super layoutSubviews];

[self.detailTextLabel sizeToFit];
self.detailTextLabel.frame = CGRectMake(self.detailTextLabel.frame.origin.x,
kVPadding,
self.detailTextLabel.frame.size.width,
self.detailTextLabel.frame.size.height) ;

self.textLabel.frame = CGRectMake(self.textLabel.frame.origin.x,
self.textLabel.frame.origin.y,
self.textLabel.frame.size.width,
self.detailTextLabel.frame.size.height);

_imageView1.frame = CGRectMake(20,
self.detailTextLabel.frame.origin.y
+ self.detailTextLabel.frame.size.height
+ kVPadding,
kImageWidth,
kImageHeight);

And in the same file, In setObject:
_imageView1.urlPath = item.image1;

I’m still working on getting the table rows to be the correct height, even though self.variableHeightRows = YES; is set.

August 26, 2010 - Paul S

You can skip those layoutView things if you add things like this to the top of the file

#import “Three20UI/TTTableCaptionItem.h”
#import “Three20UI/UIViewAdditions.h”
#import “Three20Style/UIFontAdditions.h”
#import “Three20UI/UITableViewAdditions.h”

#import “Three20Style/TTGlobalStyle.h”
#import “Three20Style/TTDefaultStyleSheet.h”

And the cell height issue means that I had to overload

+ (CGFloat)tableView:(UITableView*)tableView rowHeightForObject:(id)object
which I found by hunting around in the superclass of my TTTableCaptionItemCell subclass.

I had changed the layout of my cell so it was calculating it different. If there’s a way to get this to auto calculate the height of the cell I wish i knew about it!

Hope this helps someone
Paul


Questions? Comments? Kindly-worded insults?

Thats right, it's all welcome folks! Step right up and leave a your mark for the ages!