Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS: Pre-Calculation of Row Heights for a UITableView

I have been struggling with this for months in my project.

Here's the deal: I have a UITableView with just 1 section, but the heights of the UITableViewCells differ. Each UITableViewCell represents an object, and new objects are constantly becoming available, and inserted into the UITableView. The only way to effectively discover the height of a new object which will be displayed in the table is to draw it once (run the code that draws the UITableViewCell and look at what the resulting height is).

Ah, but there's the problem! The UITableView delegate method tableView:heightForRowAtIndexPath: is called BEFORE the cell is drawn for the first time! This makes sense: the table wants to position the cells before drawing them.

I've been experimenting LOTS and can find only 2 options, both of which have major downsides:

  1. If the new object to be shown in the table has not been seen before, simply "guess" the height and resize the cell later (after the cell is drawn) as needed. This causes problems withs scrolling though, since when the user stops scrolling it might be the case that one or more cells need to be resized to the correct height, which creates a bad user experience as the table shifts around and re-organizes itself.
  2. Pre-Calculate the object height by "testing" the object before inserting it into the UITableView. When the new object becomes available, create a "dummy" UITableViewCell and draw it off-screen, thus calculating the height of the object and saving it for later. The problem with this is that, of course, we can only do the height testing on the main thread (since we need to draw), which causes major lag in the application.

My solution thus far has been to use #2, but to try to alleviate the problem by making it so that the height-tests only happen every 1/4 of a second, which helps to "space out" the testing, so that the main thread is not locked too much. But, the solution is inelegant and still causes lag problems, especially on older devices. The lag is bad enough that I really need a better solution.

like image 225
Zane Claes Avatar asked Nov 05 '22 03:11

Zane Claes


1 Answers

For calculating the height, this and this answer suggest you could draw off-screen in a background thread using CGLayer. Link to iOS documentation to CGLayer.

And to cache the height depending on some data id that is being displayed in a cell you could use code like this:

-(CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath
{
    NSString* objectId = [self getObjectIdForPath:indexPath];
    if (![self->idToCellHeight objectForKey: objectId])
    {
        CGFloat height = [object calculateHeightForId:objectId];
        [self->idToCellHeight setObject:[NSNumber numberWithFloat:height] forKey: objectId];
    }

    return [[self->idToCellHeight objectForKey:objectId] floatValue];
}

Of course the first draw will still have to be done without knowing the exact height, but if you can't calculate the height based on the data displayed then there is no workaround.

like image 174
Greg Avatar answered Nov 09 '22 12:11

Greg