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.
- An
UIImageView
- Two variable length
UILabel
s stacked vertically on the top of each other
And I wanted tableView cells to look like this,
Nice, isn't it? Let's look at the way vertical constraints are arranged in the tableViewCell.
-
A top of
UIImageView
is attached to top ofcontentView
with fixed height -
Main label and Sub label are stacked vertically with variable height which is adjusted to the text content
-
Top of Main label is attached to top of
contentView
and bottom of Sub label is attached to bottom ofcontentView
so that cell is stretched according to size of these twoUILabel
s
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 ofcontentView
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
Let's look at other solution,
- Attaching the top and bottom of
UIImageView
to the top and bottom ofcontentView
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 forUILabel
which is expected to have variable height
- Attaching both labels and imageView to the top and bottom of
contentView
will not work either since their respective bottoms withcontentView
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 UILabel
s.
We already know the fixed height of UIImageView
. Thus we can adjust total height of UILabel
s 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
UILabel
s stacked vertically is 64 plus 10 considering top vertical padding which is 74. This meansUILabel
side maintains at least a height of 74 pixels to accommodate bothUILabel
s andUIImageView
The result is as follows,
- Not enough content on labels (Main and Sub label)
- Too much content on labels (Main and Sub label)
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