YoanGJ
YoanGJ

Reputation: 509

Why setting the font of a UIButton is messing with my layers

I'm trying to make a login register UI in Swift for iOS without using storyboard.

With this code in my controller:

    loginButton.translatesAutoresizingMaskIntoConstraints = false
    loginButton.layer.borderWidth = 1
    loginButton.clipsToBounds = true
    loginButton.layer.borderColor = UIColor.Palette.boldYellow.cgColor
    loginButton.setTitle("Login", for: .normal)
    loginButton.setTitleColor(UIColor.Palette.boldYellow, for: .normal)
    loginButton.titleLabel?.font = UIFont.boldSystemFont(ofSize: 24)
    loginButton.heightAnchor.constraint(equalToConstant: 58).isActive = true
    NSLayoutConstraint(item: loginButton, attribute: .leading, relatedBy: .equal, toItem: footerView, attribute: .leading, multiplier: 1, constant: 30).isActive = true
    NSLayoutConstraint(item: loginButton, attribute: .trailing, relatedBy: .equal, toItem: footerView, attribute: .trailing, multiplier: 1, constant: -30).isActive = true
    NSLayoutConstraint(item: loginButton, attribute: .top, relatedBy: .equal, toItem: footerView, attribute: .top, multiplier: 1, constant: 21).isActive = true

    registerButton.translatesAutoresizingMaskIntoConstraints = false
    registerButton.clipsToBounds = true
    registerButton.setTitle("Register", for: .normal)
    registerButton.setTitleColor(.white, for: .normal)
    registerButton.titleLabel?.font = UIFont.boldSystemFont(ofSize: 24)
    registerButton.heightAnchor.constraint(equalToConstant: 58).isActive = true
    NSLayoutConstraint(item: registerButton, attribute: .leading, relatedBy: .equal, toItem: footerView, attribute: .leading, multiplier: 1, constant: 30).isActive = true
    NSLayoutConstraint(item: registerButton, attribute: .trailing, relatedBy: .equal, toItem: footerView, attribute: .trailing, multiplier: 1, constant: -30).isActive = true
    NSLayoutConstraint(item: registerButton, attribute: .bottom, relatedBy: .equal, toItem: footerView, attribute: .bottom, multiplier: 1, constant: -21).isActive = true

I can achieve this:

enter image description here

But I'd like to make the "Register" text bolder so I add this line:

registerButton.titleLabel?.font = UIFont.boldSystemFont(ofSize: 24)

Just before

registerButton.heightAnchor.constraint(equalToConstant: 58).isActive = true

But then, the whole interface is broken and I got this:

enter image description here

Looks like the changes that I made to the layer of both buttons are not applied.

I do these layer changes in the viewDidLayoutSubviews override of my controller :

  override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    let gradientBackgroundButton = CAGradientLayer()
    gradientBackgroundButton.frame = registerButton.bounds
    gradientBackgroundButton.startPoint = CGPoint.zero
    gradientBackgroundButton.endPoint = CGPoint(x: 1, y: 1)
    gradientBackgroundButton.colors = [UIColor.Palette.lightYellow.cgColor, UIColor.Palette.boldYellow.cgColor]

    registerButton.layer.insertSublayer(gradientBackgroundButton, at: 0)

    registerButton.layer.cornerRadius = registerButton.bounds.height / 2
    loginButton.layer.cornerRadius = loginButton.bounds.height / 2
}

My guess is that when I don't specify the font size my view needs to call viewDidLayoutSubviews but not when I specify one. In this case where am I supposed to modify the layer of my buttons?

Upvotes: 2

Views: 114

Answers (1)

jrturton
jrturton

Reputation: 119262

I think your guess is a good one. viewDidLayoutSubviews could be called many, many times, and currently each time it's called you're going to be creating and adding a new gradient layer. Add a breakpoint to viewDidLayoutSubviews and see how often it runs and what the button size values are.

You either need to check for the existence of a gradient layer before inserting and sizing it, or ideally move all of this logic into UIButton subclasses, which can keep their own references to the layers and deal with them when the button itself is resized.

Upvotes: 2

Related Questions