Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing intrinsicContentSize correctly for sizes that depend on solved internal layout

I have a solution that seems to be working, but I want to make sure that it's safe and using the tool as intended. I can distill my problem down to this scenario - I have a UIView that contains multiple UILabels, stacked vertically. Each label can be filled with arbitrary text that wraps and expands the label vertically. The auto-layout constraints to stack the labels within the view is trivial. What I'd like to do is have the UIView communicate its preferred height (the height of the view should fit all of the labels) to its owner via intrinsicContentSize. However, the internal layout needs to be solved before I can return the appropriate height. My understanding is that the solver is guaranteed to be complete after returning from [super layoutSubviews], this is what I'm doing:

- (void)layoutSubviews
{
    [super layoutSubviews];

    // solver's complete, now we can measure?
    [self invalidateIntrinsicContentSize];
}

My intrinsicContentSize implementation refers to the last label's frame to figure out the height. This all seems to work for my current case, but it's making me nervous. It seems to be that the solver asks for the intrinsicContentSize of the current view while it's laying out its internals. I won't go into detail, but I ran into a case in which this pattern spun into an infinite loop. I was able to fix this issue by tweaking some of my layout code - this did not inspire confidence.

Is there a better way to do this? This seems like a common thing to want to do and when I started working on the problem I thought this was what intrinsicContentSize was for. The implementations of intrinsicContentSize that I've seen have always been trivial - hardcoded size for a dimension, or measuring a single view that does not depend on a solved, internal layout.

Note, I can't just use measure the labels via the NSString sizeWith... methods because the internal layout is too complex.

Note, I explored using systemLayoutSizeFittingSize:, but calling this from intrinsicContentSize results in an infinite loop.

Any response would be appreciated! Thanks in advance!

like image 513
tyler Avatar asked Nov 06 '13 18:11

tyler


1 Answers

I'd like to update this thread and close out this question - I think there's still value here for other folks just starting with AutoLayout that might have a similar misunderstanding. It's important to understand the real purpose of intrinsicContentSize - this is not how typical views communicate their preferred size to their parent views. If a view uses AutoLayout to layout its children then it will use the constraints that define its internal layout to inform its parent of its size - specifically, it's going to be the constraints that relate the child views to their parent that are going to help define the parents size. This is actually the part of AutoLayout that is both magical and frustrating - it's the sum of all of these constraints (both internal and external to your view) that result in the layout. Intrinsic size is really only necessary for cases in which your view does not use AutoLayout to define its size (such as the classic case of a UILabel or UIImageView).

I think that early iOS bugs/inconsistencies related to multi-line text were what lead me down the road to intrinsicContentSize. Multi-line text is still not perfect, but hopefully better in iOS10.

like image 148
tyler Avatar answered Nov 01 '22 16:11

tyler