Frédéric Adda Frédéric Adda - 8 months ago 116
Swift Question

Custom UIButton @IBDesignable

I created a UIButton subclass that looks like a checkmark.

Here is the class:

import UIKit

@IBDesignable
class CheckedButton: UIButton {

// MARK: - Properties
@IBInspectable var checked: Bool = false {
didSet {
// Toggle the check/uncheck images
updateImage()
}
}


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

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

private func setup() {
updateImage()
self.addTarget(self, action: #selector(tapped), for: .touchUpInside)
}

private func updateImage() {
let image = checked ? UIImage(named: "checked") : UIImage(named: "unchecked")
self.setImage(image, for: .normal)
}

/// Called each time the button is tapped, and toggles the checked property
@objc private func tapped() {
checked = !checked
print("New value: \(checked)")
}
}


Since I set the
checked
property as @IBInspectable, I see it in IB :
IBInspectable

The weird thing is:


  • if I let this property as
    default
    , it is correctly showing in the storyboard



uncheck


  • but if I choose either
    on
    or
    off
    inthe inspector, the screen is not updated properly.



empty

As the class is marked @IBDesignable, I would expect the button appearance to update in IB according to the value set for this property in the inspector tab.
Got a clue?

Answer Source

UIimage(named:) method uses main bundle but Interface Builder load resources in different way.

Try this:

UIImage(named: "checked", in: bundle, compatibleWith: nil)

 @IBDesignable

 class CheckedButton: UIButton {

 // MARK: - Properties
 @IBInspectable var checked: Bool = false {
     didSet {
         // Toggle the check/uncheck images
         updateImage()
     }
 }


 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()
 }

 internal func setup() {
     self.addTarget(self, action: #selector(tapped), for: .touchUpInside)
 }

 private func updateImage() {
     let bundle = Bundle(for: CheckedButton.self)
     let image = checked ? UIImage(named: "checked", in: bundle, compatibleWith:nil) : UIImage(named: "unchecked", in: bundle, compatibleWith:nil)
     self.setBackgroundImage(image, for: .normal)
 }

 /// Called each time the button is tapped, and toggles the checked property
 @objc private func tapped() {
     checked = !checked
     print("New value: \(checked)")
 }

just like this

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download