UITableView
UITableView
The old way
UITableView inherits from UIScrollView).
If the rows were all equal this was just a simple operation. But if they were different, it had to know the heights of all the rows and sum them. It asked us for the height of every single row using the delegate method:
Swift
1 2 | func tableView ( _ tableView : UITableView , heightForRowAtIndexPath indexPath : NSIndexPath ) -> CGFloat |
In this method we calculated the height of the row and return it to the system. This could be a long operation and resulted in latency. Some implementations used to deque an auto-layout positioned cell, configure it and call the method systemLayoutSizeFittingSizeheight
In iOS7 this process was then slightly optimized introducing the delegate method:
Swift
1 2 | func tableView ( _ tableView : UITableView , estimatedHeightForRowAtIndexPath indexPath : NSIndexPath ) -> CGFloat |
heightForRowAtIndexPath.
iOS8
The new way to do this is, as expected, simpler. Now the system does not need the overall height anymore, but it adjust the content size of the scroll view when a new row is going to be displayed. Besides it can make the calculation of the height themselves if we used auto layout to configure it.
As an example we build a table in which every row shows a different text and adjust its height based on text length.
You can find the project at the end of the article, let’s see the main things we have to pay attention to.
Defin the Cell Autolayout
We have a table view in IB containing a custom prototype cell. The cell contains two labels positioned with autolayout:
The “titolo” label is 20pt from top,leading,trailing of the superview (the content view). The “Descrizione” label is 20pt from bottom,leading,trailing of the content view. The two labels have a vertical space of 20pt between them.
Set the IB Row Heights
The TableView Row height can be set in the IB panel:
UITableViewAutomaticDimension
Anyway we set it to 103 to avoid warning in IB relative to the layout and size of the cell elements. In fact we have 3 vertical constraint with constant 20 plus two labels with height 21 each (= 20*3+21*2=102!). If you set it to 102 (instead of 103) IB complaints about auto layout inconsistencies. I think the difference (of 1pt) is due to floating calculation performed by IB, but, again, this is not important as this is only a placeholder for the runtime value.
The height of the cell in IB can be left to the the default (will be set at runtime):
The labels
We want the “Descrizione” label to span more lines, so set the “lines” field to 0, that means unlimited lines:
intrinsic content size
The “Preferred Width” of the labels must be set to “automatic”.
preferredMaxLayoutWidth
This property affects the size of the label when layout constraints are applied to it. During layout, if the text extends beyond the width specified by this property, the additional text is flowed to one or more new lines, thereby increasing the height of the label.
This width will be set at runtime based on the width of the tableview (plus the value of our horizontal constraint). The height of the labels will then be calculated based on this width and the text to fit inside the label.
The Code
The code of our project is really simple. The only thing we have to set about the table view are:
Swift
1 | tableView . rowHeight = UITableViewAutomaticDimension |
This tells the system not to call heightForRowAtIndexPath, but to use the new logic.
Swift
1 | tableView . estimatedRowHeight = 102 |
This is the value used by the system “before” making the real calculation.
And, that it’s it! These cells are “self sizing”.
Dynamic text type
This is why Apple did all this: to allow us to introduce dynamic text type in all our apps with minimal effort. And in fact the relative code is easily added to the sample app.
If the font of the labels are set using:
Swift
1 | cell . descrLabel . font = UIFont . preferredFontForTextStyle ( UIFontTextStyleBody ) |
The cells are automatically resized to adjust the new font dimension: