Sep 02

To change the height of the cells in a UITableView, use the property rowHeight. Or change the value in Interface Builder.

There is a method called heightForRowAtIndexPath in UITableViewDelegate, where you can also set the height. However this is NOT recommended. Apple’s release notes states the following about this method:

It is very, very expensive to customize row heights (via tableView:heightForRowAtIndexPath:).

It makes sense that having rows with different heights in your table will wreak havoc with the table view’s reuse of cells. But it also turns out that if you return the same value from this method for each row, you also suffer a significant performance penalty.

So just use the simple rowHeight property. It’s less code to type, and it’s significantly faster.

Thanks to Brent for the performance testing.

Addendum: If you really, really must have different row heights in your table. Then heightForRowAtIndexPath is the only way to achieve this. If your table only has a handful of rows then performance will not be a big issue. But if you have hundreds of rows, all with varying heights, then I would suggest looking at constructing the table using HTML in a web view instead.

written by Nick \\ tags: , ,

Oct 08

Creating objects can be very expensive in terms of performance. This is especially true for UIControls that are also added to a UIView. Instead create a number of objects once and reuse them when needed.

Here’s a code snippet from a book reader application that lays out each text paragraph as it’s own UILabel. Since each page has a varying number of paragraphs each with varying number of lines of text I initially created the necessary UILabels each time a page was rendered. Bad idea!

Now I have an array (often called an object pool) of UILabels that have been created and added to the page UIView. Initially the hidden property is set to YES for all these labels. When a page is rendered a UILabel is picked from the pool, initialized with the appropriate text, location and size. And the hidden property is set to NO to display the label.

// Add new text to existing labels
for (int i = 0; i < MAX_LABELS; i++) {
   UILabel *textLabel = [textLabels objectAtIndex:i];
   if (i < [currentPage.paragraphs count]) {
      NSString *paragraph = [currentPage.paragraphs objectAtIndex:i];
      CGSize textSize = [paragraph sizeWithFont:textFont
 						constrainedToSize:CGSizeMake(PARAGRAPH_WIDTH, 1000)
 						lineBreakMode:UILineBreakModeWordWrap];
      textLabel.font = textFont;
      textLabel.frame = CGRectMake(PARAGRAPH_LEFT_MARGIN, yOffset, PARAGRAPH_WIDTH, textSize.height);
      textLabel.hidden = NO;
      textLabel.text = paragraph;
      yOffset = yOffset + textSize.height + PARAGRAPH_SPACING;
   } else {
      textLabel.hidden = YES;
      textLabel.text = @"";
   }
}

 
In addition to speed performance improvements this solution also has the significant benefit of managing memory much better. 

written by Nick \\ tags:

Oct 07

The first rule of performance tuning is to measure your performance. If you don’t measure it, you don’t know if you’ve made any progress or if you’ve made it worse.

Here’s some very basic sample code you can use around the code you want to measure:

NSLog(@"Begin <your method name>");
NSDate *startTime = [NSDate date];

// Do something useful here

NSTimeInterval elapsedTime = [startTime timeIntervalSinceNow];
NSLog(@"End <your method name>");
NSLog([NSString stringWithFormat:@"Elapsed time: %f", -elapsedTime]);

Note that NSTimeInterval is specified in seconds and it has a sub-millisecond resolution. If your code segment executes in less than a millisecond then you should run your code in a loop and measure the elapsed time across 100 or 1,000 iterations to get meaningful measurements.

Important: Make sure that you do your performance testing on the actual device and not in the simulator. Some operations are 10x slower on a device than in the simulator, while others are actually faster on the device.

The NSLog statements will show up in the Xcode Console window if you launch the app on the device from Xcode. You can also view log statements as warnings in the Organizer window in the Console tab.

 

written by Nick \\ tags: , ,