1

I'm building a new iOS app with swift and trying to make a custom IBDesignable UIButton class to use in the designer. My code does not seems to work either in the builder or the app itself at runtime. The default value i gave to the IBInspectable vars does not apply to them.

@IBDesignable class MyButton: UIButton {

@IBInspectable var backColor: UIColor = UIColor.red {
    didSet {
        self.backgroundColor = backColor
    }
}

@IBInspectable var textColor: UIColor = UIColor.white {
    didSet {
        self.setTitleColor(textColor, for: .normal)
    }
}

override init(frame: CGRect) {
    super.init(frame: frame)
    setup()
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    setup()
}

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

private func setup() {
    titleLabel?.font = UIFont.systemFont(ofSize: 17)
    contentEdgeInsets = UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0)
    layer.cornerRadius = (frame.height / 2)
    setTitle(title(for: .normal)?.uppercased(), for: .normal)
}
}

I expect to see the button in the designer/app with a red background and a white text and none of these happens. I'm using Swift 4.2 and Xcode 10.1.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Eitana Aviv
  • 103
  • 1
  • 4

3 Answers3

0

add this two func s

  override func awakeFromNib() {
        super.awakeFromNib()
        self.setup()
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        self.setup()
    }

no need for init

0

Setting a property default value does not invoke the didSet clause.
You need something like:

    private func setup() {
        titleLabel?.font = UIFont.systemFont(ofSize: 17)
        contentEdgeInsets = UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0)
        layer.cornerRadius = (frame.height / 2)
        setTitle(title(for: .normal)?.uppercased(), for: .normal)
        // I would do it differently, but this is just for demo purposes
        // Also, can't assign a prop to itself, so using a temp
        var tmp = self.backColor
        self.backColor = tmp
        tmp = self.textColor
        self.textColor = tmp
    }

Additionally, Xcode is not very good with IBDesignable, in swift or objective C.

Make sure the menu item: Editor -> Automatically Refresh Views is checked.
You can also explicitly do Editor -> Refresh All Views.
From my experience - this stops working after a while, and you need to quit Xcode and restart it to make it work again.

Moshe Gottlieb
  • 3,963
  • 25
  • 41
  • It did nothing.. do you have any good and full example i could use? – Eitana Aviv Jan 20 '19 at 12:18
  • 1
    @EitanaAviv As I said, Xcode is not very good with IBDesignable. Try to make sure the specified menu item is checked, and quit Xcode and restart. I tried it with a sample project and it worked (after restarting Xcode). – Moshe Gottlieb Jan 20 '19 at 12:34
0
  1. Make sure that your button is set to type .custom in your Interface Builder. This is not obvious but the default button type is .system, which cannot be customized. I saw people having no problems with this but in my applications this was always a problem.

  2. Another problem is that if you keep your inspectable values empty, the didSet won't be called and the default value won't be applied.

    @IBInspectable var backColor: UIColor? = UIColor.red {
       didSet {
          updateBackground()
      }
    }
    
    private func updateBackground() {
       self.backgroundColor = backColor
    }
    
    func setup() {
       ...
       updateBackground()
    }
    
Sulthan
  • 128,090
  • 22
  • 218
  • 270