AutoLayout: UIView within UIView has incorrect fra

2020-02-28 19:22发布

问题:

I am working with AutoLayout in Storyboard, and everything seemed to be going fine. However, when I put one UIView within another and apply all the constraints I want to both the container and its children, I notice that the frame is incorrect in viewDidLayoutSubviews:. Rather than being the frame I would expect to be calculated given my constraints, it is some disgustingly large frame (albeit at the correct origin). For example, rather than a frame of {{26, 10}, {444, 10}} I get something like {{0, 0}, {320, 568}}.

Strangely enough, this only happens to the child UIView when I situate it within another UIView that has some constraints applied to it in relation to its superview (which is the view controller's view). As I am under the impression that I can expect my views to be properly laid out according to my constraints in the method viewDidLayoutSubviews:, I am confused as to why this is happening.

Am I making any incorrect assumptions? I would appreciate it if someone might help point me in the right direction. Thanks!

Note: This problem is completely fixed if I do my correct-frame-dependent setup in viewDidAppear:, but this is a somewhat unsatisfying workaround for me.

回答1:

Auto Layout changes to bounds are not necessarily finished in -viewDidLayoutSubviews. As put in "Advance Auto Layout Toolbox":

Constraint-based layout is an iterative process. The layout pass can make changes to the constraints based on the previous layout solution, which again triggers updating the constraints following another layout pass.

The documentation for -viewDidLayoutSubviews notes with emphasis in the original:

However, this method being called does not indicate that the individual layouts of the view's subviews have been adjusted.

So layout and constraint-based iterations in the subviews could continue to adjust their frames, but if a ViewController's view's bounds don't change, its -viewWillLayoutSubviews won't get called.

Since you say your setup is "frame-dependent" you have a couple options.

  1. Do your setup in -viewWillAppear. Better than -viewDidAppear, and since you really need everything to be done after all the layout if completed but before it appears on screen, this is a legitimate reason why the method is there. If relevant, you could try calling -isMovingToParentViewController as described in Determining Why A View's Appearance Changed.
  2. Call -layoutIfNeeded on the views you need to have the correct values where you need them to. Because this does the layout work it's expensive, and it could lead to repetitive work being done or even an infinite loop if triggered during its own layout process. But it's useful if it's needed to sync some values with Auto Layout so they can be used.

If this doesn't work let us know, it could have to do with the nature of the constraints themselves or something else. The Auto Layout iterations can be tricky to sync with. Good luck.