Saturday, June 8, 2013

UITableView tableView heightForRowAtIndexPath example in Objective C (iOS).


UITableView tableView heightForRowAtIndexPath

Asks the delegate for the height to use for a row in a specified location.

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath

Parameters
tableView
The table-view object requesting this information.
indexPath
An index path that locates a row in tableView.

Return Value of [UITableView tableView heightForRowAtIndexPath]
A floating-point value that specifies the height (in points) that row should be.

Discussion of [UITableView tableView heightForRowAtIndexPath]
The method allows the delegate to specify rows with varying heights. If this method is implemented, the value it returns overrides the value specified for the rowHeight property of UITableView for the given row.

There are performance implications to using tableView:heightForRowAtIndexPath: instead of the rowHeight property. Every time a table view is displayed, it calls tableView:heightForRowAtIndexPath: on the delegate for each of its rows, which can result in a significant performance problem with table views having a large number of rows (approximately 1000 or more).

UITableView tableView heightForRowAtIndexPath example.
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

    // We want the UIFont to be the same as what is in the nib,
    //  but we dont want to call tableView dequeue a bunch because its slow.
    //  If we make the font static and only load it once we can reuse it every
    //  time we get into this method
    static UIFont* dynamicTextFont;
    static CGRect textFrame;
    static CGFloat extraHeight;
    if( !dynamicTextFont ) {
        DetailCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
        dynamicTextFont = cell.resizeLabel.font;
        CGRect cellFrame = cell.frame;
        textFrame = cell.resizeLabel.frame;
        extraHeight = cellFrame.size.height-textFrame.size.height; // The space above and below the growing field
    }
    NSString* text = .... // Get this from the some object using indexPath

    CGSize  size = [text sizeWithFont:dynamicTextFont constrainedToSize:CGSizeMake(textFrame.size.width, 200000.f) lineBreakMode:UILineBreakModeWordWrap];

    return size.height+extraHeight;
}

Example of [UITableView tableView heightForRowAtIndexPath].
- (void)viewDidLoad
{
    [super viewDidLoad];

    NSMutableArray *mutableArray = [[NSMutableArray alloc] init];

    for (int i = 0; i < [self.tableView numberOfRowsInSection:0]; i++) {
        if (arc4random() % 5 == 4) {
            [mutableArray addObject:[LCImageCell class]];
        } else {
            [mutableArray addObject:[LCImageCell class]];
        }
    }

    self.cellTypes = [NSArray arrayWithArray:mutableArray];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if ([[self.cellTypes objectAtIndex:indexPath.row] isEqual:[LCImageCell class]]) {
        [LCImageCell height];  // class method returns static height for an image cell
    } else {
        [LCTextCell height];   // class method returns static height for a text cell
    };
}

UITableView tableView heightForRowAtIndexPath example.
// Inside tableView:cellForRowAtIndexPath:
cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
cell.textLabel.numberOfLines = self.numberOfTextRows;
// numberOfTextRows is an integer, declared in the class

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

    CGSize theSize = [theObject.theStringToDisplay sizeWithFont:[UIFont systemFontOfSize:18.0f] constrainedToSize:CGSizeMake(265.0f, 9999.0f) lineBreakMode:UILineBreakModeWordWrap];
    // This gets the size of the rectangle needed to draw a multi-line string
    self.numberOfTextRows = round(theSize.height / 18);
    // 18 is the size of the font used in the text label
    // This will give us the number of lines in the multi-line string

    if ((indexPath.section == FIXED_HEIGHT_SECTION) || (self.numberOfTextRows < 2)) {
        return 44;
        // 44 is the default row height; use it for empty or one-line cells (or in other table sections)
    } else {
        return theSize.height + 16;
        // 16 seems to provide a decent space above/below; tweak to taste
    }
}

End of UITableView tableView heightForRowAtIndexPath example article.