1.原始问题：When is layoutSubviews called?
I have a custom view that’s not getting
layoutSubview messages during animation.
I have a view that fills the screen. It has a custom subview at the bottom of the screen that correctly resizes in Interface Builder if I change the height of the nav bar.
layoutSubviews is called when the view is created, but never again. My subviews are correctly laid out. If I toggle the in-call status bar off, the subview’s
layoutSubviews is not called at all, even though the main view does animate its resize.
Under what circumstances is
layoutSubviews actually called?
autoresizesSubviews set to
NO for my custom view. And in Interface Builder I have the top and bottom struts and the vertical arrow set.
I tracked the solution down to Interface Builder’s insistence that springs cannot be changed on a view that has the simulated screen elements turned on (status bar, etc.). Since the springs were off for the main view, that view could not change size and hence was scrolled down in its entirety when the in-call bar appeared.
Turning the simulated features off, then resizing the view and setting the springs correctly caused the animation to occur and my method to be called.
An extra problem in debugging this is that the simulator quits the app when the in-call status is toggled via the menu. Quit app = no debugger.
I had a similar question, but wasn’t satisfied with the answer (or any I could find on the net), so I tried it in practice and here is what I got:
initdoes not cause
be called (duh)
layoutSubviewsto be called on the
view being added, the view it’s being
added to (target view), and all the
subviews of the target
the view having its frame set only
if the size parameter of the frame is
- scrolling a UIScrollView
layoutSubviewsto be called on
the scrollView, and its superview
- rotating a device only calls
layoutSubviewon the parent view (the
responding viewControllers primary
- Resizing a view will call
layoutSubviewson its superview
Building on the previous answer by @BadPirate, I experimented a bit further and came up with some clarifications/corrections. I found that
layoutSubviews: will be called on a view if and only if:
- Its own bounds (not frame) changed.
- The bounds of one of its direct subviews changed.
- A subview is added to the view or removed from the view.
Some relevant details:
- The bounds are considered changed only if the new value is different, including a different origin. Note specifically that is why
layoutSubviews:is called whenever a UIScrollView scrolls, as it performs the scrolling by changing its bounds’ origin.
- Changing the frame will only change the bounds if the size has changed, which is the only thing propagated to the bounds property anyway.
- A change in bounds of a view that is not yet in a view hierarchy will result in a call to
layoutSubviews:when the view is eventually added to a view hierarchy.
- And just for completeness: these triggers do not directly call layoutSubviews, but rather call
setNeedsLayout, which sets/raises a flag. Each iteration of the run loop, for all views in the view hierarchy, this flag is checked. For each view where the flag is found raised,
layoutSubviews:is called on it and the flag is reset. Views higher up the hierarchy will be checked/called first.
Some of the points in BadPirate’s answer are only partially true:
addSubviewcauses layoutSubviews to be called on the view being added, the view it’s being added to (target view), and all the subviews of the target.
It depends on the view’s (target view) autoresize mask. If it has autoresize mask ON, layoutSubview will be called on each
addSubview. If it has no autoresize mask then layoutSubview will be called only when the view’s (target View) frame size changes.
Example: if you created UIView programmatically (it has no autoresize mask by default), LayoutSubview will be called only when UIView frame changes not on every
It is through this technique that the performance of the application also increases.
For the device rotation point
Rotating a device only calls layoutSubview on the parent view (the responding viewController’s primary view)
This can be true only when your VC is in the VC hierarchy (root at
window.rootViewController), well this is most common case. In iOS 5, if you create a VC, but it is not added into any another VC, then this VC would not get any noticed when device rotate. Therefore its view would not get noticed by calling layoutSubviews.
in viewController makes it to call viewDidLayoutSubviews