4

I have this custom class for a button.

import UIKit

@IBDesignable
class CustomButton: UIButton {

    @IBInspectable var cornerRadiusValue: CGFloat = 10.0 {
        didSet {
            setUpView()
        }
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.layer.cornerRadius = 10.0
    }

    override func awakeFromNib() {
        setUpView()
    }

    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        setUpView()
    }

    func setUpView() {
        self.layer.cornerRadius = 10.0
    }

}

But the corner radius is not showing on the button in the storyboard.

I understand @IBInspectable just allows you to change the value in the inspector panel. I guess thats not what I am looking for.

I would like the corner radius to just show in storyboard when I create a button with that class. Which I thought that's what @IBDesignable does.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
KotaBear233
  • 163
  • 1
  • 9
  • 2
    Don't know if it will solve but you're not clipping the layer to its bounds which you probably need to do in order for it to display at least at runtime. Not sure about in storyboard – pbush25 Apr 21 '16 at 03:47
  • Added that and didnt fix what I wanted. Thank you though. – KotaBear233 Apr 21 '16 at 03:52
  • The problem is that your code is crashing in IB so designability fails. I've provided a version of your code that doesn't crash, so it works as expected. – matt Apr 21 '16 at 16:02

3 Answers3

13

Your code is crashing in IB, so designability fails. Here is much simpler code that works:

@IBDesignable
class CustomButton: UIButton {
    @IBInspectable var cornerRadiusValue: CGFloat = 10.0 {
        didSet {
            setUpView()
        }
    }
    override func awakeFromNib() {
        super.awakeFromNib()
        setUpView()
    }
    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        setUpView()
    }
    func setUpView() {
        self.layer.cornerRadius = self.cornerRadiusValue
        self.clipsToBounds = true
    }
}

Now the button is both inspectable and designable:

enter image description here

And it also works in the running app.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Can you specify how my answer was wrong? as you have said : "Did you actually try this? Did it round the corners in the storyboard?" – Ashish Kakkad Apr 21 '16 at 05:31
  • was very useful that you call `setUpView()` on didSet rather on init of the class. that solved the trick. Thanks. – Jad Sep 21 '16 at 11:29
  • You can just call `setNeedsDisplay()` in `prepareForInterfaceBuilder()`, remove the implementation of `awakeFromNib()`, and have the logic of updating the corner radius be directly in `didSet`. – R. Rincón Dec 28 '19 at 17:28
  • If you want to set the layer border width, you should create a separate value for it, just like the corner radius. Setting the corner radius should not modify other properties. That would be misleading and leads to spaghetti code. It would violate the principle called orthogonality. – R. Rincón Dec 28 '19 at 20:43
  • Your answer sets clipsToBounds in the property observer. It just obfuscates it a little. `cornerRadiusValue` is an unnecessarily redundant name. `setUpView` is a misleading name. Your answer adds unnecessary state by using a stored property. – R. Rincón Dec 28 '19 at 20:59
2

Try this one:

@IBDesignable
class CustomButton: UIButton {

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

}
jonaszmclaren
  • 2,459
  • 20
  • 30
1

Here's a better solution than the accepted answer:

@IBDesignable
class DesignableButton: UIButton {

    @IBInspectable private var cornerRadius: CGFloat {
        get {
            return layer.cornerRadius
        }
        set {
            layer.masksToBounds = newValue > 0
            layer.cornerRadius = newValue
        }
    }

    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        setNeedsDisplay()
    }

}

It uses a computed property to avoid duplicating state with a stored property.

R. Rincón
  • 365
  • 2
  • 13