Autolayout - stretching the view to populate the parent view

I noticed a very strange behavior when trying to populate a view with a child view using autolayout. The idea is very simple: add a subview to the view and make it use the full width of the parent view.

NSDictionary *views = @{ @"subview":subView, @"parent":self }; 

This does not work:

 [self addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[subview]|" options:0 metrics:nil views:views]]; 

The subview does not use the full width of the parent view.

But it works:

 [self addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[subview(==parent)]|" options:0 metrics:nil views:views]]; 

I would expect both to work as intended. So why does the first example not work? This is what Apple recommends in the following technical note:

https://developer.apple.com/library/ios/technotes/tn2154/_index.html

EDIT : (deleted unnecessary information)

+6
source share
3 answers

The reason this does not work as expected:

The parent view is a UISCrollView , and the horizontal limits are set to "H:|[contentView]|" , the scroll contentSize will adjust to the width requested by the contentView , and not vice versa. Thus, the auto-detection mechanism will first determine the size of the contentView , and then change the contentSize of the parent (scrollview) to the same width. If the contentWidth narrower than the parent, it will not stretch, because the contentSize of the parent (scrollview) will be reduced to the size of the contentView .

For regular views (not scrollviews), the width of the parent view is fixed, so the parent view will be displayed first, and then the child view (s).

By contentView the width of the contentView the same width as the parent scrollView, the contentView will always be the same width as the parent scroll, which was what I wanted (and expected).

+1
source

The following are the limitations that you specified for your "first example":

first case

The following are the limitations that you specified for your "second example":

second case

I see two differences in these structures, which I highlighted in red on the diagrams:

  • The first (broken) case has a limitation (0x8433f8f0) in the root directory of the UIView 0x8433f8f0, fixing its width to 320 points. This is redundant because lower-level representations are limited to 160 points each, and there are sufficient restrictions so that all the ancestors of these narrower representations have a width of 320 points.

  • The second (working) case has a restriction (0x7eb4a670) fixing the width of the lower part of the UIView 0x7d5f3fa0 to the width of DetailWeatherView 0x7d5f3270. This restriction is redundant because the left edge of 3fa0 is attached to the left edge of 3270, and the right right edge of 3fa0 is bounded by the right edge of 3fa0. I assume that the predicate (==parent) adds this restriction.

So, you might think that each case has one redundant constraint, so what? Nothing wrong, right?

Not really. In the second case, an excessive restriction is really harmless. You can change the width (or both) from WeatherTypeBoxView and AdditionalWeatherInfoBox , and you will still get a unique solution.

In the first case, an excess constraint is harmless if the width of the views does not change . If you change the width limit of 320 to f8f0 without changing the width limit of 160 on the sheets, the limit system has no solution. Autolayout violates one of the restrictions for solving the system, and this can greatly violate the limit of 320 width. And we can see that the 320 width limit with its annotation UIView-Encapsulated-Layout-Width imposed by the system to make this hierarchy of representations correspond to some container size (maybe the screen size, maybe the set container controller size).

I do not know why your first case has this additional top-level restriction, but the second does not. I am inclined to believe that you changed something else that caused this difference, but the startup is so mysterious that I am not sure about that either.

If your goal is to make this hierarchy of views, fill its container and keep the sheet view equal to the width, then get rid of the 160 width limits on the sheet views and create one width limit between them.

+4
source

EDIT: as Ken Thomas said, I'm completely wrong here!

Yes, both of the constraint systems you specify should cause the values ​​of parent and subview (or parent and contentView ) to have the same width.

So, I would ask, are you absolutely sure that what you see is that the contentView does not fill the parent? Is it possible that what you see in the broken case is that the parent element actually shrinks to fit the contents of the View, and you don't notice it because the parent has a transparent or otherwise invisible background?

To test this, set the contentView and parent different opaque background colors. If I am mistaken, you will clearly see a background area where the parent expands the width greater than the contentView. If I am right, the contentView completely covers the parent width.

That's why I (perhaps presumptuously) interrogate your stated fact about what you see:

The key difference in the log output is that for the broken case, we see an unexpected constraint that contains a UIView with a fixed width of 320:

 <NSLayoutConstraint:0x8433f8f0 'UIView-Encapsulated-Layout-Width' H:[UIView:0x841b9f40(320)]> 

What kind? Based on the constraint name β€œUIView-Encapsulated-Layout-Width”, it is associated with the UIViewControllerWrapperView and that the width is held up to 320 (iPhone width), we can deduce that this restriction is automatically created by UIKit to limit the size of the UICollectionView.view to the screen size. If you look at all the constraints, you can see that this width constraint = 320 is passed along the chain of constraints (encapsulated-view β†’ DetailView β†’ DetailWeatherView) until finally the width of the DetalWeatherView , which is your parent and which uses the supview- constraints, is determined edge-offset for hugging the contentView , so it should also get the same width of 320.

In contrast, for a case that works, you can also see the same chain of constraints, which should restrict contentView.width to equal the width of the top encapsulated layout view. But the difference is that there is no restriction anywhere that contains something with a fixed value of 320. Instead, there is only a left alignment restriction.

Thus, I would expect to see this in both cases contentView.width == parent.width , but in the broken case width = 320, while in the working case, the width is determined by internal constraints with a lower priority inside the contentView , which allow you to expand it to the value greater than 320, possibly in its intrinsicContentSize.

+2
source

Source: https://habr.com/ru/post/976261/


All Articles