Self Sizing Table View Cells — Programmatically

Satinder Singh
3 min readJul 4, 2016

--

Updates: Updated article and project to Swift 3.0

There are many posts online on how to configure a dynamic tableview cell using interface builder; however, there are not nearly as many for programmatic ones. And since we have NSLayoutAnchor, things have never been easier! By the end, you will know how to create tableview cells of dynamic height based on label content using AutoLayout.

Starter Project

To kick things off, let’s begin with the following starter project. The application has removed Storyboard and pushes the View Controller onto the navigation controller within the app delegate. The main files we will be interacting are as follows

  • Book.swift — model class containing a book’s name and details
  • BookTableViewCell.swift — Empty UITableViewCell subclass conforming to UITableViewDataSource Protocol
  • BookDataSource.swift — Contains class method for generating a book list
  • ViewController.swift — Empty ViewController subclass

Configure TableView

Within the function configureTableView(:), add the following code

tableview.estimatedRowHeight = 100tableview.rowHeight = UITableViewAutomaticDimensiontableview.registerClass(BookTableViewCell.self, forCellReuseIdentifier: bookCellReuseIdentifier)

By updating the estimatedRowHeight property, we have gained the following effect:

If the table contains variable height rows, it might be expensive to calculate all their heights when the table loads. Using estimation allows you to defer some of the cost of geometry calculation from load time to scrolling time. When you create a self-sizing table view cell, you need to set this property and use constraints to define the cell’s size.

Note we have also set the tableview’s rowHeight property. By doing so, we have can expect the self-sizing behavior for a cell. Furthermore, I have noticed some developers override heightForRowAtIndexPath to achieve a similar effect. This should be avoided for the following reason.

There are performance implications to using tableView:heightForRowAtIndexPath: instead of rowHeight. Every time a table view is displayed, it calls tableView:heightForRowAtIndexPath: on the delegate for each of its rows, which can result in a significant performance problem with table views having a large number of rows (approximately 1000 or more).

The registerClass simply registers our BookTableViewCell with an identifier.

By using NSLayoutAnchor, we will pin the tableview’s anchors to its superview. Add the following code within configureTableView(:)

view.addSubview(tableview)        tableview.translatesAutoresizingMaskIntoConstraints = falsetableview.topAnchor.constraint(equalTo: view.topAnchor).isActive = truetableview.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = truetableview.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = truetableview.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true

Now that the tableview is correctly positioned, we can focus on the cell.

Configure TableViewCell

The key is to use the correct constraints to create the dynamically sized cells. We will first configure the titleLabel by adding the following code within the provided init

let marginGuide = contentView.layoutMarginsGuide// configure titleLabelcontentView.addSubview(nameLabel)nameLabel.translatesAutoresizingMaskIntoConstraints = falsenameLabel.leadingAnchor.constraint(equalTo: marginGuide.leadingAnchor).isActive = truenameLabel.topAnchor.constraint(equalTo: marginGuide.topAnchor).isActive = truenameLabel.trailingAnchor.constraint(equalTo: marginGuide.trailingAnchor).isActive = truenameLabel.numberOfLines = 0

There are some key points to note here.

  1. I am adding the views to the contentView, not the view itself
  2. I am setting the numberOfLines to inform iOS it can be of multiple lines
  3. I am using the marginGuide’s anchor instead of the view’s anchors so the recommended padding is utilized
  4. The bottomAnchor has not been set because it will relate to the detailLabel and not the marginGuide

Now we will configure the remaining label with the appropriate constraints

contentView.addSubview(detailLabel)detailLabel.translatesAutoresizingMaskIntoConstraints = falsedetailLabel.leadingAnchor.constraint(equalTo: marginGuide.leadingAnchor).isActive = truedetailLabel.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor).isActive = truedetailLabel.trailingAnchor.constraint(equalTo: marginGuide.trailingAnchor).isActive = truedetailLabel.topAnchor.constraint(equalTo: nameLabel.bottomAnchor).isActive = truedetailLabel.numberOfLines = 0detailLabel.font = UIFont(name: "Avenir-Book", size: 12)detailLabel.textColor = UIColor.lightGrayColor

As noted above, the nameLabel’s bottomAnchor is constrained to the detailLabel’s topAnchor. That is all it really takes to create dynamically self sizing cells with AutoLayout.

Test

Now you may run the application and see the following dynamic cells in action. The complete GitHub project is available here.

Self Sizing Cells

--

--