Menu & Search

Automatically resizing UITableViewCells with dynamic text height using Auto Layout

August 16, 2015

I’ve already covered how to make UITableView Row Height Dynamic for Downloaded Images, but today I’ll show you how to properly set up automatically sized custom UITableViewCells from iOS8 onward.

This has been an issue throughout all iOS versions and most of us just went with the preferred way of implementing heightForRowAtIndexPath: method and calculating UILabel size by hand. This is now a thing of the past!

If we set up our custom cells properly using Auto Layout constraints, iOS will do all the resizing by itself and we don’t have to calculate anything anymore. Magic!

Note: this is a shorter version of the guide that assumes you already know how to set up a basic custom cell using Auto Layout. If you’re new to Auto Layout or the following things don’t ring a bell, make sure to check out my book on Auto Layout Fundamentals for iOS.

Setup

I won’t go over the whole setup here, but basically, I started with a Single View application (Swift), removed the generated ViewController.Swift and View Controller in Main.storyboard.

Then I added a new UITableViewController to the storyboard and created a UITableViewController subclass called DynamicTableViewController. Next I subclassed UITableViewCell, named it DynamicTableViewCell and added three IBOutlet UILabels. Pretty basic.

In the DynamicTableViewController, I added some test data and implemented tableView:cellForRowAtIndexPath:. Other than that, I have not done anything else to calculate or define table view’s row heights.

PS: You’ll be able to download the full project at the end of this post.

The Custom Cell

We want our cell to have three labels – one for the title, one for the date and one for the main body text which will be a bit longer and will require the cell to grow.

Note: every UILabel that will have multiple rows needs to have its Lines property in Identity Inspector set to 0. Default is 1. In our case this is the body label.

Screen Shot 2015-08-04 at 08.32.02

If you’re familiar with Auto Layout, you’ll be able to decode and reproduce these constraints:

Screen Shot 2015-08-04 at 08.08.59

Important!

The most important thing is that constraints defined through the whole vertical space of the cell. For example, if the body label wouldn’t have the bottom vertical space to the Content View, Auto Layout wouldn’t complain about it, since label has its own intrinsic content size, but the results would be well … crap:

no-bottom-constraintWith some sample data filled in, this is what we get:

dynamic-table-view-height-failNot pretty.

This is happening because we’re basically letting UITableView to draw all the rows with the default rowHeight property which in our case is 44.

Let’s re-add the bottom vertical constraint to the body label and re-run:

dynamic-table-view-height-fail-2A bit better. At least the text is not overflowing the cells, right? But now all the rows are the same height as we’ve set it in the storyboard and they’re not resizing.

To fix this and make our table view automagically resize to fit all the text, we have to add 2 lines of code in our DynamicTableViewController.swift file:

Note: estimatedRowHeight can be whatever you like, as long as it’s more than 0. If it’s 0, UIKit won’t even try to estimate how big the rows should be and just go with the default size it gets from the storyboard.

dynamic-table-view-height-fail-3Looks much better, BUT! There’s always a but somewhere.

The UIKit bug

We can see that our rows are at least 1 text line too short. The thing is that if we scroll down and up again, it’s resized properly. This seems to be a Apple bug (rdar://17799811) that was supposed to be fixed but it’s obviously not.

I’ve seen a lot of workarounds and the suggested method is to just call tableView.reloadData() in viewDidAppear(). I can’t tell you how much this sucks! Your users will see the table getting refreshed and you don’t want them to be thinking  “Wait, what? Was there something wrong? Did I do something?”

The solution

I’ll give you a much nicer solution. All you have to do is call

in viewDidLoad() in the table view controller.

⌘+R and voilà:

dynamic-table-view-height-win

 

Recap

As we’ve seen, properly set up custom UITableViewCells require little to no extra coding to achieve automatically resized table view rows.

The important thing is to set up the cell so the vertical height is unambiguous. This way, the table view will be able to calculate the size of each cell.

If all your cells have the same height, double check that you’re setting estimatedRowHeight to a non-zero amount and rowHeight  to UITableViewAutomaticDimension.

If you’re still seeing wrongly sized rows, you probably have to call setNeedsLayout() and layoutIfNeeded() on the tableView object in the viewDidLoad() method.

Here’s the complete source code on GitHub and a convenient .zip file link.

Note: this is a shorter version of the guide that assumes you already know how to set up a basic custom cell using Auto Layout. If you’re new to Auto Layout or the following things don’t ring a bell, make sure to check out my book on Auto Layout Fundamentals for iOS.

 

STOP LOSING TIME WITH AUTO LAYOUT!

Take part in the 5-day course with actionable tasks that will let you become a master at recognizing and solving the most common mistakes iOS developers do with Auto Layout.

Let Auto Layout become a tool you swing with your utmost confidence!

I won't send you spam, I promise. Unsubscribe at any time. Powered by ConvertKit
Hey there! You're already subscribed to my newsletter and you've hopefully gotten some useful tips and tricks when working with iOS. If you're also working with Auto Layout, make sure to check out my book called Auto Layout Fundamentals and get a 20% off for being my subscriber! https://gum.co/autolayoutfundamentals/youareawesome
Jure Zove

A lot of things but mostly a programmer who really likes fast cars. Check me out on Twitter, if you fancy.

Related article

How to properly do buttons in table view cells using Swift closures

Note: This post is an upgrade of the original post…

A practical MVVM example in Swift – Part 2 (featuring RxSwift)

Welcome to part 2 of the practical MVVM example in…

A practical MVVM example in Swift – Part 1

The good ol’ MVC pattern has been around for a…

  • fewspider

    thanks a lot

  • Manolo Suarez

    Hi Jure Zove, thanks for let me be part of your site. I following your post on resizing text in table view, I got how to do it, my question is how to make resizable a label that fits in portrait mode but in landscape show some text followed by “…” due to screen size. Any idea?

    • Hey Manolo! Good question – I suspect there might be something wrong with laying out labels once orientation changes. I will check this out as soon as possible and let you know.

      • Manolo Suarez

        Hi Jure, sorry if I did not make my question clear, I am looking to make an Label in a ViewController to resize when rotate, the text fits in portrait but in landscape mode at some part of the text shows (…) and the user can not read the rest of the text in landscape mode. Thank you….

        • How is this label configured? I assume it’s not in a table view? Otherwise, it really depends on the whole layout – if you have a really long text, you might introduce a scroll view or a table view … Do you have a screenshot?

    • Manolo, I just checked out the sample app that’s at the bottom of the article and rotating the device from portrait to landscape works properly – the cells are being resized as they need to be. Are you sure you are calling:

      self.tableView.setNeedsLayout()
      self.tableView.layoutIfNeeded()

      in viewDidLoad() ?

  • erril

    Thank you for this clear, concise article. I always struggle w/ auto layout

    • Thanks, happy to help! Let me know if you have any specific issues with auto layout which I could put into a post like this.

  • Mihai Fratu

    Damn! I’ve been struggling with these crazy AutoLayout cells on every project that I had been doing in the last year. For all of them I’ve been able to find some kind of work around but your solution DOES work every time.

    Thanks, you’re a god!

    • @mihaifratu:disqus you are welcome, thanks for the comment! 🙂

  • Pingback: Self-sizing UITextView in a UITableView using Auto Layout (like Reminders.app) – Candy Code()

  • Eric Whitley

    Really, really nice. Thanks for making this oh-so-easy.

  • Erum Huang

    It works very well! Solved my problem perfectly! Thanks very much!

  • Asahi Kuang

    I have embedded the UITableViewController in a navigationController.The auto resize height of cell will be failed. If scroll the tableview to reuse cells, it will be back.

    • Hey there, no idea why embedding it into a Nav controller wouldn’t work. Can you provide a sample code so I could take a look?

  • Bharath Ns

    Nice Article!. I have similar solution, but does this work for custom font? looks like it might have issues with that

    • Bharath Ns

      Looks like we need a mandatory Height constraint with lower priority for a body if we are using custom fonts.
      kind of weird that the constraint is not needed for system fonts it resizes automatically.

      • Hey there – I’m just testing the app out with custom fonts and it looks fine to me.

        I’ve created a new branch that uses OpenSans (which is not a system font or installed in my Fontbook currently) and it looks like it’s working. Can you check: https://github.com/jurezove/dynamic-uitableview-row-heights/tree/custom-fonts

        • Bharath Ns

          Thanks a lot @JureZove for your response. I need to check what I was doing wrong then.

  • Prekshya Basnet

    Thank you so much!! it worked for me 🙂

  • Konstantinos Mavroudakis

    Thank you !!! I just used the following and it worked in my case that I use the default TableViewController with Subtitle table view cell type:

    self.tableView.estimatedRowHeight = 40
    self.tableView.rowHeight = UITableViewAutomaticDimension

  • In my Project I am tried your solution using to correctly resize cell
    self.tableView.setNeedsLayout()
    self.tableView.layoutIfNeeded()

    but it didn’t worked for me.

    I found another solution which is disabling size class for storyboard and It cell started updating immediately according to content and constraint set in storyboard.

    Do you know any other good solution which doesn’t require to disable size class?

    • Thanks for sharing the solution! Unfortunately, I still haven’t found a more robust solution that wouldn’t require the size class hack. 🙁

      • Unfortunately, I also tried many other solutions but they didn’t worked and I am currently using size class hack only. If you find any robust solution for this issue then please share with community.

  • cell.layoutIfNeeded() it helps also instead of layoutif needed in ViewDidLoad, in case you use tableView.reloadData()

Type your search keyword, and press enter to search