This repo is known-good for Swift 3, Xcode 8.2.1, iOS 10.2.1
This repo shows seven examples of implementing a UITableView
with
self-sizing cells, where the row heights are calculated automatically
by using Auto Layout constraints. With these constraints, the cell
defines how tall it wants to be in order to contain its content, which
is text wrapped over multiple lines in a single UILabel
.
This repo shows seven ways to do this, by looking at different ways of instantiating the two table view controller and the cell:
-
table view controller
- programmatically
- or from a storyboard,
-
defining the cell
- purely programatically,
- programmatically but loading a contained view deined with a nib,
- entirely via a nib
- via a prototype cell within a storyboard
First, all these working examples show the two crucical steps for
self-sizing cells: setting up the constraints against the
contentView
not the cell, and setting the table view's rowHeight
and estimatedRowHeight
appropriately to trigger the self-sizing
mechanism.
Second, it shows that many commonly-recommended special measures are
not necessary, at least in the simple case of single word-wrapping
UILabel
in a cell. In particular, none of the examples do any of the
following:
- set the
preferredmaxLayoutWidth
of aUILabel
- override
layoutSubviews
- set content-hugging or compression-resistance priorities
- install multiple competing constraints to balance shrinking the cell without over-shrinking it
If you google around trying to figure out how to get your self-sizing cells to work, you'll find a lot of advice recommending these steps. These steps are sometimes necessary, but not always, and not for the reason that is often implied.
For instance, if you are working with a collection view as opposed to
a table view, then the collection view does not constrain the width,
so then you may need to use two layout passes, one pass to measure the
desired width, before setting preferredMaxLayoutWidth
based on that,
and then another pass to perform real layout once
preferredMaxLayoutWidth
has been set correctly. But this necessity
is a problem with auto layout and wrapping in the general case, which
collection views represent, not the special case of self-sizing table
view cells, where width is known and only height needs to be
calculated.
Or, if you are laying out a cell with multiple labels stacked on top
of each other vertically, then you may need to tweak
compression-resistance priorities so that Auto Layout knows which
label should take the hit when there's not enough space for both of
them to assume their intrinsicContentSize
. I am not sure. But again,
this seems like a issue general to specifying auto layout behavior
when there are multiple views at play, not a problem with self-sizing
table view cells.
This example project also includes a discussion in comments, and some
configurable code, for exploring why UITableView
s with self-sizing
rows sometimes start up by displaying spurious console warnings about
being unable to satisfy constraints that include
UIView-Encapsulated-Layout-Height.
Here are some good resources:
-
This is a great article proving why at the most fundamental level it is impossible for the auto layout algorithm alone to handle normal word-wrapping with one layout pass. It is because of this, I believe, that one does need to override
layoutSubviews
and manually setpreferredMaxLayoutWidth
when working with collection views, as opposed to table views. With table views, the UITableView object defines the width so the cell only needs to determine its height. -
The section Intrinsic Content Size of Multi-Line Text in this objc.io article on Auto Layout is a great resource for understanding why and how to adjust
preferredMaxLayoutWidth
. -
The leading Stack Overflow answer on this question, and the accompanying github repo, providing a working example of a more complex cell layout, also taking various the "special measures" to make it work.
-
This is a github repo showing that, for collection views, self-sizing cells simply do not work. Yet?