*

Our Beautiful Faces is Jeremy Hunt Schoenherr and he does iPhone development, for now.

Feb 7

DevTip: Don’t always recycle UITableViewCells

So, Our Beautiful Faces is one thing, and my day job is another. During the day I work for MLB.com, mostly working on their iPhone app At Bat and other mobile stuff. Anyway, I am in the middle of refactoring At Bat for this season, and have been trying to optimize certain things.

One thing that always needs to be optimized is table scrolling. Choppy is bad. Real bad. Drives people crazy.  And it inevitably happens. You have add a lot of subviews because there is a lot of data to display in each UITableViewCell, and eventually, you get to a point where rendering these cells take a little too long.

There’s lots of things you can do for this. You can stop using subviews and just draw everything to the cell’s contentView when drawRect is called. Sounds like a lot of work.  You can make everything opaque. This does render faster, but I’ve never noticed it making much of a difference visually. You can only update subViews if they’ve actually changed. If you have a UIImageView as a subview, don’t blindly call setImage on it over and over, unless that image is actually different.

But there’s something else you can do which is a little less obvious. Cell reuse is awesome and for the most part, should always be used. Especially for UITableView’s of arbitrary length. However, if you always have a reasonable number of cells, let’s say 5-50, and you know you will never really need more than that, then why not have all of them in memory? It’s not gonna break the bank to have them all ready so why keep making it render all the new information each time?

So, what do you need? Not much. Basically, instead of trying to dequeue cells from the UITableView, keep track of them yourself in a dictionary. See in the following code snippet.

NSMutableDictionary *myDictionary;

...

- (UITableViewCell *)tableView:(UITableView *)tableView 
         cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{

    NSString *cellId = [NSString:stringWithFormat:@"id_%d_%d];

    UITableViewCell *cell;

    if ([myDictionary objectForKey:cellId]) {

        cell = (UITableViewCell*)[myDictionary objectForKey:cellId];
        //update cell if necessary

    } else {
        cell = [self createYourCell:indexPath];
        [myDictionary setObject:cell forKey:cellId];
    }
    return cell;

}

Obviously, if you have too many cells, this won’t work. Also, make sure your prepareForReuse method on your cell isn’t wiping things out. The only reason this helps out at all, is because it doesn’t need to redraw all the subviews. So, if you wipe them all out by resetting them in your prepareForReuse, then you are defeating the purpose.

One thing, the first time you scroll, it may still be a little choppy. This is because we are still creating the cells as we scroll. Yuck. So how do we get around this? Easy, after the UITableView loads, or maybe after you have loaded your data from the network and parsed it, iterate through all the cells manually. Create an NSIndexPath for every cell you will need and then call your method that is creating the actual cell. In the example above that method is  [self createYourCell:indexPath]. Then store the result in the NSMutableDictionary. Done.

This is a simple way to make scrolling fly when developing on the iPhone SDK if you have a reasonable number of cells that don’t change too often.

Hopefully I’ll have more developer tips as time go on, but I found this particular thing to really help me out, and hopefully it will help you as well.

//jeremy


Comments (View)
blog comments powered by Disqus
Page 1 of 1