質問

I'm trying to make sense of an autolayout issue. I have a small custom view that itself has an imageView and a couple of labels. The image is fetched from a server and includes some metadata to help the app determine where the text should be placed. For now, I just need to limit the width of the labels (triggering word wrap) to avoid putting text over top of a logo in the background.

To make this easier, I thought I could create a containing view for the two labels. That way, I could resize the container and (I thought) the sub-views would follow suit.

Screenshot from Xcode

I'm pretty sure I have the constraints set up correctly, because it all works OK until I start fiddling with the frame:

Image before fiddling

When the image is fetched from the server, I resize the frame as follows:

CGRect frame = self.labelContainerView.frame;
frame.size.width = my_limited_width;
self.labelContainerView.frame = frame;

And I can see that the container itself has been resized, but its child views haven't:

Resized container with un-resized child views

To try to trigger the layout, I've done:

[self.labelContainerView setNeedsLayout];
[self.labelContainerView layoutIfNeeded];

... but this ends up just restoring the original constraints from the .xib. That's a handy feature and all... and it's one that I might need... but not what I need here.

I've seen posts that hint that resizing frames is the wrong thing to do here; that you should get a handle on the appropriate constraint and fiddle with it instead. I tried that:

self.rightMarginConstraint.constant = 150;

... and it almost works:

Adjusted constraint on right edge of superview

But in this case, that first (larger) label never grows beyond two lines, even if its height constraint is set to be <= something ridiculously huge. The layout system isn't re-computing how many lines the label will need.

Obviously I don't understand enough about the way views, frames, and autolayout work together to be able to see the solution for myself, which is why I'm asking here. What's the correct way to solve this problem?

役に立ちましたか?

解決

As @MatíasR noted in the comments, the proper way to do this is to manipulate the constraint rather than setting the frame directly.

I submitted this to Apple Developer Technical Support (DTS) and received the answer to the second part of my question. When the layout occurs, the labels need to have their preferredMaxLayoutWidth updated:

- (void)viewDidLayoutSubviews
{
    self.titleLabel.preferredMaxLayoutWidth = self.titleLabel.bounds.size.width;
    self.subtitleLabel.preferredMaxLayoutWidth = self.subtitleLabel.bounds.size.width;
    [super viewDidLayoutSubviews];
}

I still had to force a new layout, but that was easy:

self.rightMarginConstraint.constant = 150;
[self.labelContainerView setNeedsLayout];
[self.labelContainerView layoutIfNeeded];

Thanks to @MatíasR for helping eliminate a variable. Once I knew the correct approach, I was able to submit a very specific chunk of sample code to DTS.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top