开发者

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:UILineBreakModeWordW开发者_如何学JAVArap];

    return labelSize.height + 20;
}


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.


The -tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath method is invoked before the scroll view is composed.

The purpose of calling this method before calling -tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath and not fired during the scrolling is that the table view (Which is inherited from UIScrollView) need to know the whole height of the contentView. Once the table view knows all the height, it cached the height before you call -reloadData

Your problem means you need to clear the content in the cell's -prepareForReuse and call -setNeedLayout to layout all the new contents.


you can use tag for your label to avoid messed up to each other

UILabel *label= (UILabel*)[cell viewWithTag:2];
label.lineBreakMode = UILineBreakModeWordWrap;
label.numberOfLines = 0;
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜