jonchaf
jonchaf

Reputation: 166

How to dynamically set the corner radius of a UIButton?

I have a UIButton in a storyboard that automatically resizes based on the device's current orientation. I have a 1:1 aspect ratio constraint on the button, and the button's corner radius should always be half its width so that it displays as a circle.

I have tried to update the corner radius in viewDidLayoutSubviews as follows:

override func viewDidLayoutSubviews() {
    bigButton.layer.cornerRadius = 0.5 * bigButton.bounds.size.width
}

I would expect this to update the corner radius based on the button's width after it resizes. I think what it's actually doing is updating the corner radius based on the button's previous width, causing it to display incorrectly.

Is there an easy way to get the button's width after it resizes? Or is there a different approach to achieve my desired effect? Thanks for the help!

Upvotes: 2

Views: 4376

Answers (2)

mrfour
mrfour

Reputation: 1258

You can make a subclass of UIButton and set the layer.cornerRadius in layoutSubviews.

class RoundButton: UIButton {
    override func layoutSubviews() {
        super.layoutSubviews()

        layer.cornerRadius = bounds.height / 2
    }
}

Here is a simple example could be tested in the Playground. You can see the corner radius is reactive every time it changes the size by clicking.

import UIKit
import PlaygroundSupport

class MyViewController: UIViewController {
    private var button: RoundButton!

    override func loadView() {
        let view = UIView()
        view.backgroundColor = .white

        button = RoundButton()
        button.contentEdgeInsets = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.addTarget(self, action: #selector(tapButton), for: .touchUpInside)
        button.setTitle("Round", for: .normal)
        button.backgroundColor = .red
        view.addSubview(button)

        NSLayoutConstraint.activate([
            button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            button.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        ])

        self.view = view
    }

    @objc private func tapButton(_ sender: Any) {
        button.contentEdgeInsets.top += 5
        button.contentEdgeInsets.bottom += 5
    }
}

class RoundButton: UIButton {
    override func layoutSubviews() {
        super.layoutSubviews()

        layer.cornerRadius = bounds.height / 2
    }
}

PlaygroundPage.current.liveView = MyViewController()

Upvotes: 6

jonchaf
jonchaf

Reputation: 166

I was able to achieve the desired effect by setting the corner radius asynchronously:

override func viewDidLayoutSubviews() {
    DispatchQueue.main.asyncAfter(deadline: .now()) {
        self.bigButton.layer.cornerRadius = 0.5 * self.bigButton.bounds.size.width
    }
}

The rotation animation becomes a little clunky, but it should be sufficient for now.

Upvotes: 1

Related Questions