5

Using swift, I tried with this code to make a button rounded, and it works:

button.layer.borderColor = UIColor.grayColor().CGColor
button.layer.borderWidth = 1
button.layer.cornerRadius = 8

Unfortunately, I've got a lot of buttons and I would like to know if there's a way to make all buttons rounded without doing "copy and paste" of the code above every time.

Cristik
  • 30,989
  • 25
  • 91
  • 127

5 Answers5

14

You can do this via UIAppearance, which is a proxy that allows you to configure properties for all objects of a UIKit class.

Firstly, as UIAppearance works on properties on the UIView itself and the ones you want to control are on the button layer, you need to expose this to the appearance:

@objc extension UIButton {
    dynamic var borderColor: UIColor? {
        get {
            if let cgColor = layer.borderColor {
                return UIColor(CGColor: cgColor)
            }
            return nil
        }
        set { layer.borderColor = newValue?.CGColor }
    }

    dynamic var borderWidth: CGFloat {
        get { return layer.borderWidth }
        set { layer.borderWidth = newValue }
    }

    dynamic var cornerRadius: CGFloat {
        get { return layer.cornerRadius }
        set { layer.cornerRadius = newValue }
    }
}

in Swift, the dynamic keyword instructs the compile to generate getters and setters for the property, so that UIAppearance that identify it as configurable, (see this SO question, and Apple's documentation for more details).

You can then set the properties on the ui appearance of the UIButton class:

UIButton.appearance().borderColor = UIColor.grayColor();
UIButton.appearance().borderWidth = 2;
UIButton.appearance().cornerRadius = 20;

You should do the configuration during the application startup, as the appearance changes apply when the view is added to the window (see the note on the UIAppearance documentation).

If you want to give these default properties only for some buttons in your class, you can subclass UIButton, and use the appearance on that class instead of UIButton. For example:

class MyButton: UIButton {}

...

MyButton.appearance().borderColor = UIColor.grayColor();
MyButton.appearance().borderWidth = 2;
MyButton.appearance().cornerRadius = 20;

will apply the styling only to buttons of the MyButton class. This allows you to define different look&feel for buttons in your class by simply subclassing UIButton and working on the appearance of the subclass.

Cristik
  • 30,989
  • 25
  • 91
  • 127
  • It works!! For all newbies (like me) I suggest to paste the code in the first pane in Appdelegate.swift just below "import UIKit" and the code in the second pane below "func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {". Thank you very much! – Stefano Ramilli Jan 15 '16 at 14:28
  • @Cristik Hello, about "`UIAppearance` works on properties in `UIView`", could you explain more? Because I tried to expose `layer.borderColor` which is `CGColor` and had runtime error. But `layer.borderWidth` can be directly exposed, how come since both of them are not properties in `UIView`? – Desmond DAI Jan 27 '18 at 09:58
  • @DesmondDAI I meant that UIAppearance needs some existing properties on `UIView` to work with. In this case if you need to configure the border color via `UIAppearance`, then you need a `borderColor` property to work with. – Cristik Jan 27 '18 at 18:44
  • @Cristik I figured out why: There are certain data types that `UIAppearance` can work with, according to https://developer.apple.com/documentation/uikit/uiappearancecontainer. And because `CGColor` is a structure therefore cannot it will fail. – Desmond DAI Jan 28 '18 at 06:55
  • Looks like properties' attributes should be `@objc dynamic...` (doesn't work without `@objc` on Swift 5) – Anton Belousov Sep 22 '19 at 19:34
  • 1
    Good point, @AntonBelousov, thanks, I added `@objc` to the extension declaration, this should make all properties from there as `@objc`. – Cristik Sep 23 '19 at 17:15
2

With this, you can apply to all buttons. Copy/paste to your helper and use.

You can modify button from a storyboard. Just, add this class to any button. (Add this class to any button in a storyboard) enter image description here

@IBDesignable
class RoundedButton: UIButton {

    override func awakeFromNib() {
        super.awakeFromNib()

        layer.cornerRadius = frame.size.height / 2
        clipsToBounds = true
        imageView?.contentMode = .scaleAspectFit
    }

    @IBInspectable var borderWidth: CGFloat = 0 {
        didSet {
            layer.borderWidth = borderWidth
        }
    }

    @IBInspectable var borderColor: UIColor? {
        didSet {
            layer.borderColor = borderColor?.cgColor
        }
    }

    @IBInspectable var bgColor: UIColor? {
        didSet {
            backgroundColor = bgColor
        }
    }

    override var isHighlighted: Bool {
        didSet {
            if isHighlighted{
                backgroundColor = backgroundColor?.alpha(0.6)
            }else{
                backgroundColor = backgroundColor?.alpha(1)
            }
        }
    }
}
tBug
  • 789
  • 9
  • 13
1

You should subclass UIbutton. In your subclass, override awakeFromNib and paste the code there. In Storyboard, when referring to your button, open the assistant editor and choose the third tab in. You can provide your custom class name here.

BenJammin
  • 1,479
  • 13
  • 18
  • Just to comment in relation to the other suggestions saying to use UIAppearance: this solution will let you apply the style to a large number of buttons, while still giving you the option to have regular UIButtons without the style later on, so I think this solution is a little more flexible (and easier imo). – Connor Neville Jan 14 '16 at 21:00
  • 1
    @ConnorNeville you can also subclass `UIButton` and configure the `UIAppearance` proxy for that subclass, this will allow different subclasses to all look the same without interfering with the default buttons – Cristik May 05 '17 at 06:08
1

Use this code to get story board outlet as property

extension UIView {
@IBInspectable var cornerRadius: CGFloat
    {
    get {
        return layer.cornerRadius
    }
    set {
        layer.cornerRadius = newValue
        layer.masksToBounds = newValue > 0
    }
}
@IBInspectable var cornerWidth: CGFloat
    {
    get {
        return layer.borderWidth
    }
    set {
        layer.borderWidth = newValue
    }
}
@IBInspectable var borderColor: UIColor
    {
    set{ self.layer.borderColor = newValue.cgColor }
    get{ return UIColor(cgColor: self.layer.borderColor!) }
}

}

Nrv
  • 280
  • 1
  • 6
0

Select the button and go to the Identity inspector, click the Add button (+) in the lower left of the user defined runtime attributes editor. Double click on the Key Path field of the new attribute to edit the key path for the attribute to layer.cornerRadius Set the type to Number and the value to 8. To make a circular button from a square button, the radius is set to half the width of the button. Or you can add this code, I find the first way easier but you can decide which one to use.

button.layer.borderWidth = 2
button.layer.cornerRadius = 8
button.clipsToBounds = true
Katz
  • 826
  • 3
  • 19
  • 40