*

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

Mar 26

DevTip: Abuse threads

Sometimes your interfaces can become clunky. You hit a button, or slide a slider and things get all stuttery. No good. People hate that and so should you. Usually this is because when your control (button, slider, tab, etc) calls back to your handler, there is too much work going on.

Just start a new thread. This sounds simple, but sometimes people over look it. I ran into this when I was adding the volume control on the new audio stream player for MLB.com At Bat. I thought, yeah, no problem, I can make a call to the audio session and store the value in my user defaults in real time. Easy! But no, it made the volume control sticky, so I had to spawn threads. See below.

In Interface Builder, connect your control to your handler for the given event the IBAction below, and then in that method, start a thread and don’t wait around for the result.

- (void) threadedTargetMethod:(id)object {
   //Do you time consuming stuff
}


- (IBAction) targetMethod:(id)sender {
   [self performSelectorOnMainThread:@selector(threadedTargetMethod:) 
                          withObject:nil 
                       waitUntilDone:NO];
}


Comments (View)
Feb 20

DevTip: Always check your distribution builds

Old post, but very useful, Craig Hockenberry walks you through testing your distribution builds.  Do this to make sure you know exactly what you are submitting to iTunes Connect.  Sometimes build settings vary between targets/build configs, and the only way to know what you have is to run what you submit.


Comments (View)
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)
Page 1 of 1