From the blog

Leveraging Auto Layout for Dynamic Cell Heights

Note: much of this technique is unnecessary now that iOS 8+ supports self-sizing cells.

Like most people, I now think strictly in constraints and have made autoresizing masks a thing of the past. Discovering the joy of auto layout with the introduction of Xcode 5 has been amazing, but I’ve found there are still a few common tasks auto layout couldn’t solve.

Like providing the layout for cells in a UITableView.

The introduction of auto layout improved on this, but still fell a little short. Since our cell knows all the information to layout correctly, why not have our cell do the size computation using auto layout?

The Method

Create a static instance of our cell, and usesystemLayoutSizeFittingSize: on UIView to get back a CGSize of what auto layout tells us our view’s size should be. The only pitfall is speed.

A UITableView calls its delegates tableView:heightForRowAtIndexPath: method multiple times for each cell when it first computes its content size and draws out its initial cells. Unfortunately, auto layout takes a relatively long time to compute the size of the cell.

To speed up the additional calls to tableView:heightForRowAtIndexPath: we can cache the cell heights once we compute them. I decided to generalize this technique into its own utility class, RZCellSizeManager.

RZCellSizeManager will calculate and cache all of your cells’ sizes. It’s created to be used with auto layout, but can work well in any case where cacheing heights can improve performance.


There are two different approaches to using RZCellSizeManager.

One, use a ReuseIdentifier to register cell class.

This is a familiar approach since it is how we normally interact with a UITableView or a UICollectionView.

RZCellSizeManager* sizeManager = [[RZCellSizeManager alloc] initWithCellClassName:@"TableViewCell" cellReuseIdentifier:[TableViewCell reuseIdentifier] configurationBlock:^(TableViewCell* cell, id object) {
    [cell setCellData:object];

If you pass in a reuseIdentifier of nil it will work fine as long as you only have one cell type.

The second approach is to register with an object class.

RZCellSizeManager* sizeManager = [[RZCellSizeManager alloc] initWithCellClassName:@"TableViewCell" objectClass:[CellData class] configurationBlock:^(TableViewCell* cell, id object) {
    [cell setCellData:object];

Either method supports the ability to register additional cell classes, either for additional ReuseIdentifiers or object classes.

- (void)registerCellClassName:(NSString *)cellClass

- (void)registerCellClassName:(NSString *)cellClass
forReuseIdentifier:(NSString *)reuseIdentifier

The setCellData: method on the cell is pretty simple.

// Using AutoLayout
- (void)setCellData:(CellData *)cellData
    self.titleLabel.text = cellData.title;
    self.descriptionLabel.text = cellData.subTitle;

Getting the Height

Simply ask the RZCellSizeManager for your height, given an object.
For the ReuseIdentifier approach, pass in an additional parameter as the ReuseIdentifier for the cell we want to use to get our height based off the indexPath.

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    // Retrieve our object to give to our size manager.
    id object = [self.dataArray objectAtIndex:indexPath.row];

    return [self.sizeManager cellHeightForObject:object indexPath:indexPath cellReuseIdentifier:[TableViewCell reuseIdentifier]];

For the object class approach, RZCellSizeManager will figure out the correct cell to use based on the class of the provided object so this call is even simpler.

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    // Retrieve our object to give to our size manager.
    id object = [self.dataArray objectAtIndex:indexPath.row];

    return [self.sizeManager cellHeightForObject:object indexPath:indexPath];

Since the size manager will cache every height it computes, future look up times will be instant.

Changing Data

It’s not realistic to assume that all data in your UITableView is static. You may have web requests loading new data or you may use core data and your model updates after initial layout. In these cases, you need to be able to clear out your cache of cell heights so they can be recomputed with the new data.

For this, there are three methods:

- (void)invalidateCellHeightCache;
- (void)invalidateCellHeightAtIndexPath:(NSIndexPath *)indexPath;
- (void)invalidateCellHeightsAtIndexPaths:(NSArray *)indexPaths;

RZCellSizeManager offers flexible cache invalidation methods that should support any optimization scenario your app requires.

There are more in-depth examples of how to use RZCellSizeManager in the Github demo project, including one using an NSFetchedResultsController and multiple cell types.

Get the Code

RZCellSizeManager is available on Github, and distributed under the MIT license.


  1. Are there any special things to consider when using this with a collection view?
    The size manager returns 0.0 for all my cells…

  2. Thanks for this! It took me a while to get it working for me, because I didn’t have any constraints set in my table cells, and it turns out that I needed at least a bottom-to-superview and width set, for some reason that I don’t want to understand :-). But without your code, I wouldn’t have gotten far enough to even discover that. iOS is so weird to me…

  3. Hello,

    Is this also valid for custom cells? For example two images are one under another and aligned to left and at the same time, title and description text exist and they are centered.

  4. Hi
    Is there a way to simplify your class by not using CellData class, but instead populate cell content directly in the TableViewCell custom class ?
    In other words, use setCellData() in the TableViewCell class ?

    Thanks for your great work !

    1. Hi Jerome,

      The CellData class is just a made up class, it isn’t fixed for CellSizeManager. You can use anything you want to configure your cell. The only important thing to do is to use a similar method both in cellForRowAtIndexPath and in the configurationBlock. For example your custom TableViewCell could have a property on it for an NSDictionary, you could then call `setDictionary` on the Cell to calculate the height.

Leave a Reply

Your email address will not be published.