PreferredMaxLayoutWidth: Everything You Need to Know

Default blog image of logo on green

In this article, we explain one of the most confusing UILabel properties - preferredMaxLayoutWidth.

I’d never been fond of this particular property, solely because of the all too familiar warning it generates.

However, not to be daunted (and with the encouragement of fellow Brightec-er Adrian), I set to work researching how to make best use of the preferredMaxLayoutWidth property.

What is PreferredMaxLayoutWidth?

PreferredMaxLayoutWidth is a UILabel property that arises from iOS6 for Objective-C. It calculates the required height for multiline labels and changes the height automatically.

I remember the bad ol’ days when we needed to use some reasonably complicated methods to dynamically calculate the right height. Nowadays we simply need to assign the desired max UILabel width and, as if by magic, auto layout will change the label size!

From Apple docs:“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.”

So, Why the Warning then?

The warning is only referring to the “Automatic” option. If you only build your project with a deployment target of iOS8+, you should never see this warning. However, if you want to run your project in iOS7, the warning will appear.

The following is from an Apple engineer:

“iOS 8 added support for automatically computing preferredMaxLayoutWidth at runtime, which makes creating multiline labels even easier.

This setting is not backwards compatible with iOS 7. To support both iOS 7 and iOS 8, Xcode 6 allows you to pick either "Automatic" or "Explicit" for preferredMaxLayoutWidth in the size inspector.

You should: Pick "Automatic" if targeting iOS 8 for the best experience.

Pick "Explicit" if targeting < iOS 8. You can then enter the value of preferredMaxLayoutWidth you would like set.

Enabling "Explicit" defaults to the current bounds size at the time you checked the box.

The warning will appear if; (1) you're using auto layout, (2) "Automatic" is set for a multiline label [you can check this in the size inspector for the label], and (3) your deployment target < iOS 8.

It seems the bug is that this warning appears for non-autolayout documents. If you are seeing this warning and not using auto layout you can ignore the warning.”

How to Fix the Warning for Versions Prior to iOS8?

How to fix the warning for versions prior to iOS8? Since we usually don’t know the intrinsic content size of UILabel and UITextField in advance, we need to take a two-step approach to get this right.

Firstly, we let Auto Layout do its work, and then we use the resulting frame in the layout pass to update the preferred maximum width and trigger layout again.

From objc.io webpage, the developer Florian Kugler basically suggests to let the Auto Layout do its work and use the resulting frame to update the preferred maximum width.

- (void)layoutSubviews { [super layoutSubviews]; myLabel.preferredMaxLayoutWidth = myLabel.frame.size.width; [super layoutSubviews]; }

The first call to [super layoutSubviews] is necessary for the label to get its frame set, while the second call is necessary to update the layout after the change.

If we omit the second call we get a NSInternalInconsistencyException error because we’ve made changes in the layout pass which require updating the constraints, but we didn’t trigger layout again.

We can also do this in a label subclass itself:

@implementation MyLabel - (void)layoutSubviews { self.preferredMaxLayoutWidth = self.frame.size.width; [super layoutSubviews]; } @end

In this case, we don’t need to call [super layoutSubviews] first, because when layoutSubviews is called, we already have a frame on the label itself. To make this adjustment from the view controller level, we hook into viewDidLayoutSubviews.

At this point the frames of the first Auto Layout pass are already set and we can use them to set the preferred maximum width.

- (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; myLabel.preferredMaxLayoutWidth = myLabel.frame.size.width; [self.view layoutIfNeeded]; }

Lastly, make sure that you don’t have an explicit height constraint on the label that has a higher priority than the label’s content compression resistance priority. Otherwise it will trump the calculated height of the content.

But...

Whilst researching this I of course I put all into practice, and it turns out that there is an undocumented feature in iOS 7 that makes the calculations for us. This means that Florian's trick is not needed anymore (only in iOS versions prior to iOS7)!

Some developers take another route to fix the problem and adopt a slightly more 'hacky' solution: They set the number of lines to 1 in Interface Builder and then they change from code to 0. Though this works, we wouldn't follow this sort of method at Brightec as it does seem a little unprofessional.

In Summary

I hope this article has been informative. I know how tricky PrefferedMaxLayoutWidth can be! But hopefully my tips help you get to grips with it faster.

For more articles like this one, check out our blog. Or browse our portfolio to see our work in action!



This article was originally written for Brightec by Jose Martinez


Looking for something else?

Search over 400 blog posts from our team

Want to hear more?

Subscribe to our monthly digest of blogs to stay in the loop and come with us on our journey to make things better!