Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS - heightForRowAtIndexPath does not fire during scrolling

I am writing a simple iOS app using Xcode4, which uses a table view to display a list of stories (fetched from a URL). I'm displaying the story titles as UILabels, and as a subview of the table cell.

I am over-riding heightForRowAtIndexPath to calculate the correct height for the cells according to the length of each story title. I'm adding the label to the cell in cellForRowAtIndexPath. When I run the app in the simulator, everything is rendered well. However: when I scroll down and scroll up, the labels get messed up. They get truncated and over-run. I debugged a little, and found that the heightForRowAtIndexPath method is not fired during scrolling, so the cell heights are not re-calculated, and therefore the label text overflows, and gets rendered ugly. Here is the relevant code:

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

    UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];

    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"Cell"];
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
        [cell autorelease];
    }

    /* NOTE: code to load trimmedTitle dynamically is snipped */
    NSString* trimmedTitle;

    UIFont *cellFont = [UIFont fontWithName:@"Georgia" size:14.0];
    CGSize constraintSize = CGSizeMake(280.0f, MAXFLOAT);
    CGSize labelSize = [trimmedTitle sizeWithFont:cellFont constrainedToSize:constraintSize 
                                    lineBreakMode:UILineBreakModeWordWrap];

    UILabel* tempLabel = [[UILabel alloc] initWithFrame:CGRectMake(60, 0, 230, labelSize.height)];
    tempLabel.lineBreakMode = UILineBreakModeWordWrap;
    tempLabel.text = trimmedTitle;
    tempLabel.numberOfLines = 0;

    [cell.contentView addSubview:tempLabel];
    [tempLabel release];

    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString* cellText;  // code to load cellText dynamically is snipped off
    UIFont *cellFont = [UIFont fontWithName:@"Georgia" size:14.0];
    CGSize constraintSize = CGSizeMake(230.0f, MAXFLOAT);
    CGSize labelSize = [cellText sizeWithFont:cellFont constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];

    return labelSize.height + 20;
}
like image 246
jeffreyveon Avatar asked Nov 04 '22 13:11

jeffreyveon


1 Answers

In situations like these I typically pre-calculate the heights of all of my rows, store them in an array, and then just returns those heights in heightForRowAtIndexPath. This way the tableview knows the height of each cell and cells be conform to that height even after reuse. I don't know of a way to force a calculation of the cell height beyond looking for when a cell will be viewable and reloading it, which seems too costly.

Update: some example code:

I have a method called - (void)calculateHeights which does the same calculation you had in heightForRowAtIndexPath, but stores the result in my mutable array heights_ ivar:

- (void)calculateHeights {
    [heights_ removeAllObjects]
    for (Widget *myWidget in modelWidgetArray) {
        NSString* cellText;  // code to load cellText dynamically is snipped off
        UIFont *cellFont = [UIFont fontWithName:@"Georgia" size:14.0];
        CGSize constraintSize = CGSizeMake(230.0f, MAXFLOAT);
        CGSize labelSize = [cellText sizeWithFont:cellFont constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];

        [heights_ addObject:[NSNumber numberWithFloat:labelSize.height + 20.0f]];
    }
}

And then in heightForRowAtIndexPath, given a 1-section table view:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return [heights_ objectAtIndex:[indexPath row]];
}

If your table view has more than one section you'll need to do some math to convert to the one-dimensional heights_ array and back again. Also, any time you -reloadData you'll need to -calculateHeights as well.

like image 182
kevboh Avatar answered Nov 09 '22 13:11

kevboh