iOS AutoLayout multi-line UILabel

ancajic picture ancajic · Jul 5, 2013 · Viewed 49.6k times · Source

Following question is sort-of continuation of this one:

iOS: Multi-line UILabel in Auto Layout

The main idea is that every view is supposed to state it's "preferred" (intrinsic) size so that AutoLayout can know how to display it properly. UILabel is just an example of a situation where a view cannot by itself know what size it needs for display. It depends on what width is provided.

As mwhuss pointed out, setPreferredMaxLayoutWidth did the trick of making the label span across multiple lines. But that is not the main question here. The question is where and when do I get this width value that I send as an argument to setPreferredMaxLayoutWidth.

I managed to make something that looks legit, so correct me if I am wrong in any way and tell me please if you know a better way.

In the UIView's

-(CGSize) intrinsicContentSize

I setPreferredMaxLayoutWidth for my UILabels according to self.frame.width.

UIViewController's

-(void) viewDidLayoutSubviews

is the first callback method I know where subviews of the main view are appointed with their exact frames that they inhabit on the screen. From inside that method I, then, operate on my subviews, invalidating their intrinsic sizes so that UILabels are broken into multiple lines based on the width that was appointed to them.

Answer

nevan king picture nevan king · Nov 29, 2013

There's an answer this question on objc.io in the "Intrinsic Content Size of Multi-Line Text" section of Advanced Auto Layout Toolbox. Here's the relevant info:

The intrinsic content size of UILabel and NSTextField is ambiguous for multi-line text. The height of the text depends on the width of the lines, which is yet to be determined when solving the constraints. In order to solve this problem, both classes have a new property called preferredMaxLayoutWidth, which specifies the maximum line width for calculating the intrinsic content size.

Since we usually don’t know this value in advance, we need to take a two-step approach to get this right. First 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.

The code they give for use inside a view controller:

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

Take a look at their post, there's more information about why it's necessary to do the layout twice.