Building Totem 2.0:
Dynamic UITableViewCell Height for Custom Cells in iOS 7

Many iOS apps like Twitter (and my upcoming Secular Totem 2.0 app) use UITableViewControllers to display lists of data. One of the trickier aspects of programming these interfaces is implementing variable height cells based on their content.

Demo

Apple provides some of the functionality to provide this capability, like UILabel’s sizeThatFits method. But that only provides a way for controls to resize, not reposition. And the positioning control by itself is not adequate for the job. And since the cell’s height callback function is called prior to the cell being created, referencing the current cell within that function isn’t possible without creating an infinite callback loop.

So to solve this issue, developers typically create ornate class methods to read in cell data and create composite labels and strings that mimic the behavior of the final output, calculate the dimensions, add in padding, and determine the height. This can be relatively effective, but sometimes a cell will get rendered without enough height or with a bit too much, due to the differences in how a control like a UILabel renders versus a simulation created in code.

I’ve found that the ideal method is to create a single prototype of the custom UITableCell (a property), provide it with the data, tell it to render, and have the cell report back the height it needs. This method renders the same cell object seen on-screen, but behind the scenes, and returns the exact measurements every time. For the sake of this post, I will concentrate on determining the cell height. But this method can also be used to get the width needed.

Create a Custom UITableCell
There are plenty of tutorials out there which describe how to create a custom UITableCell class and XIB file. So I won’t go into that here. Below are my example header and class files for one such custom cell. Keep in mind that the code examples use ARC, of course.

Notice that there are properties (outlets) for the three objects on the cell: Title, Subtitle, and ContentText. They are all UILabel controls. The last is a property which will hold the height required to render the cell. It uses a primitive type (float) so it is merely nonatomic and not a pointer.

The process within the cell’s layoutSubviews method is as follows:

  1. Tell each UILabel how to resize on its own (with size restrictions like a fixed width and variable height) once the cell is created and is loaded with content via the outlet properties
  2. Re-position the Subtitle and ContentText (bottom two) labels to accommodate growth in the Title and Subtitle labels
  3. Add up the heights of the labels, along with the padding above, below, and between the labels

FilterListCell

As seen in the image, the fields are all 220 pixels in width. So we use that as a fixed value in our calculation. The height should be boundless, so we specify the constant CGFLOAT_MAX. I’m using 15 as the padding amount at the top and bottom. There is 3 padding between the Title and Subtitle, and 7 padding between the Subtitle and ContentText.

Usage: Create a Prototype Cell
For this example I’m using a XIB with a UIView that has a UISearchBar and UISearchDisplayController. The view is basically blank, and I’m invoking a search with dummy data to render my custom cells in the searchResultsTableView.

ViewController

The header and class file follow.

The key in the header is the cellPrototype declaration. That’s our prototype cell to be used for calculating height.

There are two key code blocks to notice here. First, within viewDidLoad the prototype cell is created. The second is within the heightForRowAtIndexPath callback function, where the prototype cell is loaded with the same data the real cell will receive in the cellForRowAtIndexPath callback function, and then it is forced to render in memory by calling the layoutSubviews method within the FilterListCell class.

Conclusion
This is a basic example that uses inefficient, separate methods to provide data for the labels, etc. But it’s meant to illustrate the implementation and provide a springboard for your own tests.

I had to turn off comments due to SPAM, so if you have any feedback, please contact me using the contact form.

And best of luck!