Monday, May 23, 2011

Using custom Cells in your UITableView via Interface Builder

One of the first things I tried when developing iOS was making a custom row.
Why? you may ask, the fact that the default one only provided title, subtitle and an image wasn't enough.

The first serious app I developed for iOS was Traffic Status (or Estado del Transito in spanish); this app required a certain extra amount of info be displayed inside the cell so that the user could get quick glimpse of what the status of a certain avenue or subway line was.

I investigated several alternatives trying to figure out what to do here until I settled for a method that gets the view from a nib and iterates its children to find the correct subview.

The solution is as follows:

  • Try to reuse a cell previously stored by the TableView
  • If there is no such cell then call the customViewHelper
  • Load the top level objects from a given nib, in our case this nib will be named the same as the class we want to load into a View
  • Iterate through it's top level objects
  • When an object matches the subclass of the UITableViewCell we are looking for that object is returned
What we will obtain after this is the following:

Focus on the cells, how to do the header will come in the next Blog post


Let's go through this process step by step...

Create the UITableViewCell subclass in XCode

Go to XCode and create a new UITableViewCell subclass and call it "TransitStatusTableViewCell". This subclass will contain the following Outlets:

@property (nonatomic, retain) IBOutlet UILabel *firstLineLabel;
@property (nonatomic, retain) IBOutlet UILabel *secondLineLabel;
@property (nonatomic, retain) IBOutlet UILabel *snippetLabel;
@property (nonatomic, retain) IBOutlet UIImageView *backgroundImageView;



Make sure to release this properties when you dealloc your object and to synthesize each of them in your .m.

Now that you have your UITableViewCell Subclass you can create the corresponding .xib.
For this create a new Interface File in an Empty XIB. 
Create a UITableViewCell element from the library and tune it to your liking; but remember three important things
  1. The class of the UITableViewCell must be set to your class
  2. Set the reuse identifier or else your table view will be slow as hell
  3. The outlets are connected from the UITableViewCell to the elements, the File owner, unlike what happens in most cases, plays no part in creating custom views
You should end up with something like this





Create the CustomViewHelp Class in XCode


We are creating this Helper class to save us the work of iterating through subviews every time we want a custom view. This class one very simple to understand method and should be implemented like this


+ (id)customViewForClass:(Class)customClass {
    NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass(customClass) owner:nil options:nil];
    for ( id currentObject in topLevelObjects ) {
        if ( [currentObject isKindOfClass:customClass] ) {
            return currentObject;
        }
    }

    return nil;
}




Instantiating and filling the customCell


With both our custom cell and the Helper out of the way the only thing left to do is to actually plug this baby into a UITableViewController (or a UIViewController with the delegate methods).
With that in mind we will go straight to the tableView:cellForRowAtIndexPath: method and put our brand new cell to the test.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSString *cellIdentifier = NSStringFromClass([TransitStatusTableViewCell class]);

    TransitStatusTableViewCell *cell = (TransitStatusTableViewCell *) [tableView dequeueReusableCellWithIdentifier:cellIdentifier];

    if (cell == nil) {
        cell = [CustomViewHelper customViewForClass:[TransitStatusTableViewCell class]];
        cell.backgroundColor = [UIColor clearColor];
    }

    // This is to change the background image depending on if the cell is the last cell or any other
    // These two images should be created in the viewDidLoad method

    if (indexPath.row == [[dataArray objectAtIndex:indexPath.section] count] - 1) {
        cell.backgroundImageView.image = lastRowImage;
    } else {
        cell.backgroundImageView.image = anyRowImage;
    }

    // Fill your cell with whichever info you want
    // For example:
    // cell.firstLineLabel = @"Effective Mobility";

    return cell;
}





Conclusion


What you end up with is a really simple and reusable customCell just like the one you can see on the newest version of Estado del TrĂ¡nsito (right now it's pending approval by Apple).


No comments:

Post a Comment