iOS - Autolayout and Resizing UILabel (iOS/Swift)

Last week I had weird issue while applying autolayout rules to UITableViewCell. TableViewCell has two components attached to it.

  1. An UIImageView
  2. Two variable length UILabels stacked vertically on the top of each other

And I wanted tableView cells to look like this,

cell_with_image_and_labels_demo

Nice, isn't it? Let's look at the way vertical constraints are arranged in the tableViewCell.

  1. A top of UIImageView is attached to top of contentView with fixed height

  2. Main label and Sub label are stacked vertically with variable height which is adjusted to the text content

  3. Top of Main label is attached to top of contentView and bottom of Sub label is attached to bottom of contentView so that cell is stretched according to size of these two UILabels

Having looked at primary setup, let's look at the problem with this approach.

  • Attaching top of Main label with top of contentView and bottom of Sub label with bottom of contentView

self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[imageV(74)]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))
self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[mainLabel]-[subLabel]-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))
  • This approach will work well when content of labels is well enough to stretch the UITableViewCell to vertically accommodate imageView. But, if content of labels is not enough, cell with shrink so as to eclipse the remaining height of imageView as follows

issue_with_current_autolayout

Let's look at other solution,

  • Attaching the top and bottom of UIImageView to the top and bottom of contentView respectively.

self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[imageV(74)]-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))
self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[mainLabel]-[subLabel]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))
  • This approach will not work either from following screenshot since bottom of imageView is attached to bottom of contentView, we will no longer have variable height cell since height of imageView is always the same which will cause visual issue for UILabel which is expected to have variable height

current_autolayout_issue_2

  • Attaching both labels and imageView to the top and bottom of contentView will not work either since their respective bottoms with contentView will cause an autolayout ambiguity for variable length labels

self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[imageV(74)]-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))
self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[mainLabel]-[subLabel]-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))

Solving autolayout issue

Now, let's look at it how it can be solved to accommodate fixed height UIImageView as well as variable height UILabels.

We already know the fixed height of UIImageView. Thus we can adjust total height of UILabels to be minimum of height of UIImageView so that even if there is no enough content to UILabel, it will stretch to at least minimum height to accommodate the imageView height.


self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[imageV(74)]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))
self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[mainLabel(>=32)]-[subLabel(>=32)]-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))
  • Total height of UIImageView is 74 plus top vertical padding of 5 pixels which is fixed

  • Total minimum height of two UILabels stacked vertically is 64 plus 10 considering top vertical padding which is 74. This means UILabel side maintains at least a height of 74 pixels to accommodate both UILabels and UIImageView

The result is as follows,

  • Not enough content on labels (Main and Sub label)

not_enough_content

  • Too much content on labels (Main and Sub label)

too_much_content

Hope this will help someone someday. Let me know if you have come up with any other solutions to tackle the similar problem. Autolayout is fun, not because it's confusing, but it instills an inspiration to look for innovative solutions. Obviously, the beauty of it is there is no single solution, but there could be many. That's why I am looking forward to hear from you soon