Expanding UITableView cells using only constraints in Swift

Here is a common requirement you may have stumbled upon a few times when creating an iOS app.

First, the result we want to achieve.

Final result

Let’s hop into Xcode and create a new “Single View Application” project. We’re going to begin by designing our interface. 
A simple UINavigationController with our ViewController as its root will do.

Main.storyboard

Let’s add our UITableView and add an UITableViewCell to it. We need two labels, one that will be our title, and a second one below that will be our subtitle that will be either shown or hidden.

As we want for AutoLayout to compute the cell height itself, we need to setup the constraints accordingly.
Add a leading and trailing constraint to both labels. Then we want to add a top constraint between the title and its superview, a bottom constraint between the subtitle and its superview, and a vertical spacing constraint between both labels.
This will allow AutoLayout to understand that we want the superview to grow based on our labels intrinsic height. We’ll also set both label numberOfLines property to zero, so they don’t get truncated.

Constraints on our ExpandingTableViewCell

At that point AutoLayout may display an error, asking you to set a different vertical hugging priority to one of the labels. Seems logic, as for now both have the same priorities and AutoLayout need to know which one should take precedence. We can decrease the vertical hugging priority or set a fixed height constraint to either labels in order to get rid of this error.

Our UI seems good for now, let’s dive into the code! First, we need to create a new class for our prototyped UITableViewCell. We can call it ExpandingTableViewCell for the sake of originality. 
Add both labels outlet and link them to their actual view in InterfaceBuilder.

Now, it’s a good idea to have a backing class for our cell. You can use a simple dictionary, but having typed properties is always a plus. To accurately represent our cell state we need a title, subtitle and a boolean to check wether the cell is expanded or not.

ExpandingTableViewCell.swift

Now that this is taken care of, onto our ViewController!

We want a property linked to our UITableView, and another for the array that will contain our cell representations. Let’s populate it with a few ExpandingTableViewCellContent instances to it so we can have something to display later.

ViewController.swift

We still have to implement the datasource for the UITableView, and more importantly the delegate, where the magic happen!
In our delegate method didSelectRowAtIndexPath, we want 2 things : 
- Change the expanded state of our backing class at that particular index
- Reload the cell to reflect its new state

UITableViewDataSource and UITableViewDelegate implementation in ViewController.swift

Alright, everything seems in place, let’s build & run (command + R)!

Nope

Whoops, that’s not what we want! Well, we miss one last detail in our ViewController viewDidLoad to make this a home run. We need to explicitly specify to our UITableView that AutoLayout should take care of our cell heights. This is done in 2 lines :

tableView.estimatedRowHeight = 60
tableView.rowHeight = UITableViewAutomaticDimension

This time the code should run smoothly and behave correctly. Enjoy!

Hope you liked this tutorial, you can find its source-code on github. Don’t hesitate to like/comment if you found it useful!