iOS8 Self Sizing UITableView Cells

The UITableView is a class we all have used at least once in our apps.

Sometimes we need a UITableView with cells all equal in height, but sometimes we need dynamic height cells, each one having a different height, typically dependent on its own content.

The old way

Before iOS8, this was not trivial since the system needed to know in advance the overall height of the table in order to set the content size of the scrolling area (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:

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 systemLayoutSizeFittingSize to obtain the space occupied by the cell, then returned to the system the height field of this size.

In iOS7 this process was then slightly optimized introducing the delegate method:

This delegate method was supposed to be very fast and return a “raw” estimation of the height (no slow calculations). It was used by the system to estimate the overall height of the table. The precise height of a row was then asked at the time the row was displayed using the other (slow but precise) 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:

Schermata 2014-09-18 alle 18.20.47

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:

Schermata 2014-09-18 alle 18.27.41

This value is not very important because it is only a placeholder to work in IB. Since we will use the value UITableViewAutomaticDimension (see after), the real value will be decided at runtime.

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):

Schermata 2014-09-18 alle 18.36.05

The labels

We want the “Descrizione” label to span more lines, so set the “lines” field to 0, that means unlimited lines:

Schermata 2014-09-19 alle 16.11.18

Then we must say to IB that the intrinsic content size is different from what we are seeing in UB, it will be determined at runtime (based on text displayed) and the one of IB is only a placeholder:

Schermata 2014-09-18 alle 18.33.33

The “Preferred Width” of the labels must be set to “automatic”.

Schermata 2014-09-18 alle 18.52.19

More on this: how can the system know how much is the label height? The property preferredMaxLayoutWidth is so defined:

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:

This tells the system not to call heightForRowAtIndexPath, but to use the new logic.

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:

The cells are automatically resized to adjust the new font dimension:

Schermata 2014-09-18 alle 18.42.34

Schermata 2014-09-18 alle 18.42.52

Schermata 2014-09-18 alle 18.43.08

To be notified when the user changes the text dynamic type in the settings app, you must register for the notification UIContentSizeCategoryDidChangeNotification and reload table data in the observer callback.

Sample Project: SelfSizingCells

Valerio Ferrucci

Valerio Ferrucci (valfer) develops software on Apple Macintosh since the 1990s until today for MacOS, OSX and, since some years, iOS. He is also a Web (PHP/MySQL/JS/CSS) and Android Developer.

More Posts - Website

Follow Me:
TwitterFacebookLinkedIn

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

7 thoughts on “iOS8 Self Sizing UITableView Cells”

  1. Thanks for these valuable information:)
    I have a problem with UITableView. I think it is somehow related to the dynamic sizing issues you described above. So I will be really happy if you are able to share your ideas about my case.

    I have a tableView with long rows and I want to make it horizontally scrollable. I know that UITableView is a subclass of UIScrollView, thus what I need is just to set its contentSize.width to a bigger-than-screen-width value so that I can scroll horizontally.
    My problem is, even if I set the contentSize.width in viewDidLoad method of UITableViewController class, it changes back to a small value after table loaded. If I change the value after table loaded, than, I really make it horizontally scrollable but there is no content after going to right a little. (I think this is as expected since such amount of data loaded to table.)
    So, I need a proper way of changing contentSize.width of tableView. Do you have any ideas on this?

    Best Regards,
    Hasan

    1. I think the problem is related to the fact that the labels have a “preferredMaxLayoutWidth” property that adjust automatically when the UITableView layout itself. I would try to subclass UITableViewCell and set the label’s preferredMaxLayoutWidth manually in the subclass layoutSubviews method.

      1. In fact I already subclassed UITableViewCell and inserted multiple label which are placed horizontally to get a excel sheet like view. In other words, there is not a single label in the cell but multiple labels (i.e. 15 labels per cell ). I will check the “preferredMaxLayoutWidth” property, but i think it is somehow different in my case.

  2. What if i have 3 labels and i want to grow them all according to their contents, but not shrink them, also cell’s height should adjust according to these 3 labels

    Can you help me with this?

Leave a Reply to hasan Cancel reply

Your email address will not be published. Required fields are marked *