1

I am trying to make a custom UIButton subclass that has different colors in the normal, selected, and disabled states. My button lives in a framework that is then imported into an app, but every code snippet I place here, I have tried in both the main app and the framework--I know it shouldn't make any difference, but I wanted to cover my bases. I can't get it to work to save my life.

class BrokenButton: UIButton {
    override var isEnabled: Bool {
        didSet {
            print("This is never called no matter what I do")
        }
    }
}

I've tried using KVO to watch the value of isEnabled since overriding the setter did not work:

class BrokenButton2: UIButton {
    required init() {
        super.init(frame: .zero)
        addObserver(self, forKeyPath: #keyPath(isEnabled), options: [.new], context: nil)
    }

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
        print("Never called")
    }
}

I'm at my wit's end here. What am I getting wrong about this?

  • try using open keyword before class BrokenButton and for overriden property isEnabled. Plus make sure to change the Subclass type to BrokenButton if you're using storyboard or xib. – Suhit Patil Dec 19 '17 at 18:47
  • Did you try change status of button? e.g. for isSelected put button.isSelected = !button.isSelected inside button action and then your didSet start recognize state changes. – Mateusz Dec 19 '17 at 19:12
  • When using KVO you are using the obj-c runtime. `isEnabled` should be `enabled`. `brokenButton.observe(\BrokenButton2.enabled, options: [.new, .old, .initial]) { (button, _) in print("Did set enabled \(button.isEnabled)") }` works fine for me as did your `didSet` property observing code. – beyowulf Dec 19 '17 at 19:25
  • @suhit, can you re-post your comment as an answer so that I can accept it and close the question? On adding the 'open' keyword, the compiler got annoyed at me and requested several other changes, and after I updated my class to comply, I started hitting the breakpoints I had added to `didSet`. – Daniel Burbank Dec 19 '17 at 20:22
  • How do you call `isEnabled`? – meaning-matters Dec 19 '17 at 20:31
  • @DanielBurbank added the answer as per your comments. – Suhit Patil Dec 20 '17 at 07:16

2 Answers2

3

@Daniel As the BrokenButton class is inside the Framework, you need to use open keyword for accessing from outside the other modules. So just add the open keyword before the BrokenButton class and isEnabled property.

open class BrokenButton: UIButton {
    override open var isEnabled: Bool {
        didSet {
            print("This is never called no matter what I do")
        }
    }
}

An open class is accessible and subclassable outside of the defining module. An open class member is accessible and overridable outside of the defining module.

for further info regarding open keyword..read this stackoverflow answer

Suhit Patil
  • 11,748
  • 3
  • 50
  • 60
0

I figured the same as meaning-matters. Here are some steps you can take to reproduce a way in which it works. It's possible you missed any one of these steps.

  1. Create BrokenButton class which is a subclass of UIButton. Just as you did in your question above.

  2. Open up storyboard or xib and drag a UIButton into your storyboard or xib

  3. Select the UIButton you just dragged into the storyboard/xib, and in the identify inspector, make sure you make the class BrokenButton

  4. Create an Outlet in your ViewController something like this: @IBOutlet weak var button: BrokenButton?

  5. In your storyboard/xib, connections inspector, connect the button to your IBOutlet

  6. Then in your viewcontroller, set the button to be enabled or disabled like this: button?.isEnabled = true

  7. Here it is in the works.enter image description here

Gabriel Pires
  • 926
  • 9
  • 12
  • This can't be correct because other behavior of the button DOES happen. Print statements in the constructor work, and my observers for UIControlEvents 'touchDown', 'touchUpInside', 'touchUpOutside', and 'touchCancel' all work fine. When I override 'isEnabled' in my class in the same way that you did in this sample, with a print statement and a breakpoint in didSet, neither the print statement nor the breakpoint is hit. – Daniel Burbank Dec 19 '17 at 20:07
  • I, my own self, am not. This button is meant to be used by engineers within my company that are following design guidelines. The intention is that the button can be configured with a pattern name, and then it will represent itself in all states (normal, selected, disabled) with the correct colors. – Daniel Burbank Dec 19 '17 at 20:18
  • I see, I misunderstood your question then. It seemed to me what you really wanted was to get this `print("This is never called no matter what I do")` line to be called. I still do not fully understand, but it seems you have found your answer based on your other comments. – Gabriel Pires Dec 19 '17 at 20:48