Search code examples
uiviewuiviewcontrollerautolayoutlayoutsubviewsviewdidlayoutsubviews

where to put manual layout code for a UIView where parent views use autolayout?


Where to put manual layout code (or where to trigger it) for a specific child UIView I have, when the parent views themselves utilise autolayout?

Notes:

  1. layout code for the view needs to use its frame dimensions after any parent view layout, orientation change, trait change etc
  2. I had tried using viewWillLayoutSubviews on the parent viewcontroller but was having difficulty where it was called more than once at times - couldn't track down why however do NOT want to have to do manual layout calculations more than once as they are onerous.
  3. So overall a way to for the various scenarios below end up with only one single "triggering" of the manual layout AND at a time when all rotation & parent view layout has been done:

So:

  • on load (viewDidLoad)
  • redisplay (like view did appear subsequently)
  • rotation
  • trait change

For example should I be looking at a way for:

a) parent view controller to control this and determine when to send a "layoutSubviewNow" call to my specific child view (which is fronted by it's own viewcontroller), but in which case how to do this, or b)

b) leaving it the child view to react on a "layoutSubview()" and then have a delegate available to call back to the main controller to request layout data?, or

c) similar to b) but using the child views view controller "viewWillLayoutSubviews()" to trigger and then have a delegate available to call back to the main controller to request layout data?

or other?

actually I'm thinking of this idea:

d) in the main parent view controller: have a instance variable viewTransistionedOutstandingAction, and use it such that (i) in viewWillTransition set this to true, then in (ii) viewWillLayoutSubviews do the following:

if viewTransistionedOutstandingAction {
  self.triggerModelChange()
  viewTransistionedOutstandingAction = false
}

Solution

  • It's a little unclear to me what exactly your requirements are but I can provide an answer for your very first question:

    Where to put manual layout code (or where to trigger it) for a specific child UIView I have, when the parent views themselves utilise autolayout?

    There is exactly one place in a UIView implementation where you should put any layout code and that is inside the method layoutSubviews().

    The layout engine (the thing that computes the actual frames from your constraints) calls this method right after it has computed the correct frame for this particular view. With other word: layoutSubviews() is the one and only place during the layout process where you can be sure that the view already has its correct frame set.

    You are correct that this method might be called not just once but several times. That's because in some cases several layout passes are needed to compute the final layout. (Example: You have some view whose height depends on its superview's width but the superview's height depends on the subview's height.) The layoutSubviews() method might also be called manually from code, e.g. calling setNeedsLayout() followed by layoutIfNeeded() on a view will result in a layoutSubviews() call on that view as well.

    However, the system calls layoutSubviews() only as often as it needs to (unless you manually trigger it over and over again) i.e. whenever the current layout is invalidated. There can be many causes for this: Device rotations, constraint changes, changes in the view hierarchy etc. But these are the cases where your view's frame might change and if your custom layout depends on that frame it actually should be recomputed as well.

    For that reason layoutSubviews() is just the right place to perform your manual layout and you should refrain from doing it anywhere else.


    Note: If you're dealing with view controllers instead of plain views the corresponding method to perform your layout updates is viewWillLayoutSubviews() (or viewDidLayoutSubviews() if you are using Auto Layout and only want to make slight modifications after the layout engine has resolved the constraints for all subviews).