When Is Layoutsubviews Called

When is layoutSubviews called?

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.

When is the layoutSubviews method called?

You will find your answer here

EDIT: copied directly from the blog:

  • init does not cause layoutSubviews to be called (duh)
  • addSubview causes 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 view
  • setFrame intelligently calls layoutSubviews on the view having it’s frame set only if the size parameter of the frame is different
  • scrolling a UIScrollView causes layoutSubviews to be called on the scrollView, and it’s superview
  • rotating a device only calls layoutSubview on the parent view (the responding viewControllers primary view)
  • removeFromSuperview – layoutSubviews is called on superview only

Is layoutSubviews called whenever view's size is changed?

According to sources at Apple,

"-[UIView layoutSubviews] should get called when the size of the view changes."



They also referred me to this, from the the View Programming Guide for iOS:

"Whenever the size of a view changes, UIKit applies the autoresizing behaviors of that view’s subviews and then calls the layoutSubviews method of the view to let it make manual changes. You can implement the layoutSubviews method in custom views when the autoresizing behaviors by themselves do not yield the results you want."



At this point, your best move is to create a small sample project where layoutSubviews does not get called (or, send your existing project) file a bug with Apple using BugReporter, and include that sample project with your bug.

layoutSubviews getting called repeatedly when the app enters background state

In your test project, if I run on an iPhone, I'm not seeing layoutSubviews() being called in the background. It only happens on iPad.

That's because your app supports Multitasking:

  • When your app is deactivated, iOS resizes it into different sizes in order to take snapshots of it for the app switcher. Those resizes cause your view's layoutSubviews() to be called. That's completely normal.
  • iOS then returns your app to the original size.

The real problem is that you are creating a "layout loop". Your code in layoutSubviews() is causing your own view's layout to be invalidated, so the system needs to run the layout process again. Then layout runs, you do it again, and it happens all over again.

Specifically, the cause is:

self.font = UIFont(name: fontName, size: fontSize)

Changing your label's font causes its intrinsicSize to change, which means that its superviews may need their layout to be updated, so the layout process needs to run again. It's a bad idea to do this in layoutSubviews() because it causes layout loops. You should really only change properties of your subviews, not your view itself.

Why do you think you need to do this in layoutSubviews()? There is probably a better place to put it, outside of the layout process. In your example, I don't see how this code does anything useful at all.

It would make more sense to set adjustsFrameSizeToWidth once, and then don't do anything in layoutSubviews():

override init(frame: CGRect) {
super.init(frame: frame)
self.adjustsFontSizeToFitWidth = true
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.adjustsFontSizeToFitWidth = true
}

If you're trying to change your font's size depending on the size class, you can do it in code by overriding traitCollectionDidChange():

override func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)

var fontSize: CGFloat
if (self.traitCollection.horizontalSizeClass == .Regular) {
fontSize = 70
}
else {
fontSize = 30
}
self.font = UIFont(name: fontName, size:fontSize)
}

iOS: determine when all children have had layoutSubviews called

You shouldn't have to know if the subviews of a subview have updated their layout: That sounds like too tight coupling. Also, each subview might handle the arrangement of their respective subviews differently and might not (need to) call layoutSubviews for its subviews at all. You should only ever have to know about your direct subviews. You should treat them more or less as black boxes and not care whether they have subviews of their own or not.

layoutSubviews called twice when rotating

I had the same question. I found this page to be helpful for me. Is this useful for you?

EDIT:
Here is the summary of the page (copied):

  • init does not cause layoutSubviews to be called (duh)
  • addSubview causes 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 view
  • setFrame intelligently calls layoutSubviews on the view having it’s frame set only if the size parameter of the frame is different
  • scrolling a UIScrollView causes layoutSubviews to be called on the scrollView, and it’s superview
  • rotating a device only calls layoutSubview on the parent view (the responding viewControllers primary view)
  • removeFromSuperview – layoutSubviews is called on superview only (not show in table)

UIView layoutSubviews not being called

Your problem here is you've implemented -layoutSubviews on your view controller, instead of on your view. It will never be called this way. -layoutSubviews is a mechanism by which a custom view can lay out its own subviews (e.g. a UIButton that has a UIImageView for the image and a UILabel for the label, and uses -layoutSubviews to ensure the imageview and label are all positioned correctly). It's not a mechanism by which a view controller can control the layout of its views.

If you want to change layout on rotation, then you should just go ahead and set up the new layout inside of -didRotateFromInterfaceOrientation:.

What's exactly viewDidLayoutSubviews?

When bounds change for a ViewControllers View, this method is called after the positions and sizes of the subviews have changed.

  1. So this is our chance to make changes to view after it has laid out its subviews, but before it is visible on screen.

  2. Any changes that depending on bounds has to be done, we can do here and not in ViewDidLoad or ViewWillAppear.

  3. While ViewDidLoad & ViewWillAppear, the frame and bounds of a view are
    not finalised. So when AutoLayout has done it's job of fixing mainView and
    it's subviews, this method is called.

When using autolayout, framework does not call layoutSubviews every time. This is called in these cases.

  • Rotating a device: only calls layoutSubview on the parent view (the responding viewControllers primary view)

  • Its own bounds (not frame) changed. (The bounds are considered changed only if the new value is different, including a different origin.)

  • A subview is added to the view or removed from the view.
  • Your application forces layout to occur by calling the setNeedsLayout or layoutIfNeeded method of a view.
  • Scrolling a UIScrollView causes layoutSubviews to be called on the scrollView, and its superview.

Note:
The call for viewDidLayoutSubviews also depends on various factors like autoresize mask, using Auto-Layout or not, and whether view is in view hierarchy or not.

For any other clarification, check When is layoutSubviews called?

How to force layoutSubviews of UIView?

Had a similar problem today. I solved it by first calling

  • -setNeedsLayout: Set the flag that the view needs layout, and afterwards calling
  • -layoutIfNeeded: Check the flag immediately and - as it was set manually before - as result layoutSubviews is called.

Don't know if this is the best way, but it works ;)



Related Topics



Leave a reply



Submit