Viewwilllayoutsubviews in Swift

When are the ViewWillLayoutSubviews and ViewDidLayoutSubviews methods called?

From the documentation:

When a view's bounds change, the view adjusts the position of its subviews. Your view controller can override this method to make changes before the view lays out its subviews. The default implementation of this method does nothing.

viewWillLayoutSubviews gets called anytime your view controller's view has its bounds changed. This happens when the view is loaded, when a rotation event occurs, or when a child view controller has its size changed by its parent. (There are probably some other situations, too). If there is anything you need to update before that view lays itself out (and before your constraints are re-applied) you should do it here. you should generally not update constraints here, because updating constraints can cause another layout pass.

viewDidLayoutSubviews is called once all of your subviews have been laid out. If you need to fine-tune that layout by manually adjusting frames, for instance, this would be the place to do it.

View controller lifecycle can be a bit confusing, but it's worth trying to really understand if you're going to be doing much iOS development. This article is a really good overview.

Setting corner radius through viewDidAppear() or viewWillLayoutSubviews()?

Use viewDidLayoutSubviews instead, because you'll have the updated bounds at that point, but not yet in viewWillLayoutSubviews.

Or I might suggesting have a button subclass that does the rounding within its own layoutSubviews, freeing your view controller from having to iterate through buttons and rounding them.

For example:

@IBDesignable
class RoundedButton: UIButton {

override func layoutSubviews() {
super.layoutSubviews()

let radius = min(bounds.width, bounds.height) / 2
layer.cornerRadius = radius
}

}

And then just use this class instead of UIButton. That yields:

Sample Image

UIButtons setAttributedTitle Disappears After Calling layoutSubviews in UIButton Extension

Your code as it stands is dangerous and illegal:

extension UIButton {
override open func layoutSubviews() {

No! You cannot perform an override in an extension! The results can be unpredictable. The way to achieve a reliable circular button is to subclass UIButton, where override is legal, and use that subclass.

class MyCircularButton: UIButton {
override open func layoutSubviews() {
super.layoutSubviews()
let radius = min(bounds.width, bounds.height) / 2
layer.cornerRadius = radius
}
}

Now use a MyCircularButton instead of a plain UIButton wherever you want a circular button. I don't know that this will solve the issue you're having, but it is certainly a required first step.

I tested your remaining code like this, and it worked fine:

class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let tag1_Button = MyCircularButton()
tag1_Button.frame = CGRect(x: 100, y: 100, width: 40, height: 40)
tag1_Button.titleLabel?.textAlignment = .center
tag1_Button.contentHorizontalAlignment = .center
tag1_Button.titleLabel?.numberOfLines = 1
let str_tag1_Button = NSMutableAttributedString(string: "AC")
str_tag1_Button.addAttribute(.font, value: UIFont.systemFont(ofSize: 10), range: NSMakeRange(0, 2))
str_tag1_Button.addAttribute(.foregroundColor, value: UIColor(red: 0/255.0, green: 0/255.0, blue: 0/255.0, alpha: 1.0), range: NSMakeRange(0, 2))
tag1_Button.setAttributedTitle(str_tag1_Button, for: .normal)
tag1_Button.backgroundColor = UIColor(red: 40/255.0, green: 247/255.0, blue: 45/255.0, alpha: 1.0)
self.view.addSubview(tag1_Button)
}
}

Result:

Sample Image

What kind of things can cause viewWillLayoutSubviews to be run in the background thread?

This kind of crash occurs when you call something in the background that must run on the main thread.

Finding the exact line may be almost impossible. I found the result with lots of NSLog lines and [NSThread isMainThread] lines.

Calling almost anything on UI elements (especially those require size calculations/changes, triggers viewWillLayoutSubviews. In my case it was both UIImageViews and UILabels.

If you can't find where you make UI updates in the background, check if you post NSNotifications in the background.

how not to fall into infinite loop with viewWillLayoutSubviews?

You should take this functionality out of viewWIllLayoutSubviews and put it into it's own function. Call that function for your animation, and call the setNeedsLayout in your animation block. viewWillLayoutSubviews can be called for many reasons.

The tableView beginUpdates and endUpdates notify the view that the tableView, or parts of it need to be redrawn, so it will call viewWillLayoutSubviews.



Related Topics



Leave a reply



Submit