-3

I want to create a custom view that display it when an action occurs (like changed property) on another object (subclass of UIControl)

My approach was it

  1. create a protocol whose UIControl objects can conform
  2. create my custom view in which I can observe my delegate

Unfortunately, it's not work, worse, it's crash because compiler say "its not kvc-compliant"

Bellow, my code :

protocol CustomDelegate: class where Self : UIControl {
    func respondeToControl() -> Bool
}
class CustomView: UIView {
    weak var delegate: CustomDelegate? {
        didSet{
             observeIfControlIsPressed()
       }
   }

   func observeIfControlIsPressed(){
       if delegate != nil {
            let keypath = delegate! as! UIControl

            keypath.addObserver(keypath,
                            forKeyPath: "backgroundColor",
                         options: [NSKeyValueObservingOptions.new, NSKeyValueObservingOptions.old],
            context: nil)

        }
   }

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

My question is, how I can re-design my solution to make it work ?

A.A Noman
  • 5,244
  • 9
  • 24
  • 46

2 Answers2

0

You addObserver is wrong, the "observer" should be self no "keypath"

Try this:

keypath.addObserver(self, forKeyPath: "backgroundColor", options: [.new, .old], context: nil)
Kevinosaurio
  • 1,952
  • 2
  • 15
  • 18
0

You did not read your error correctly.. For example:

protocol CustomDelegate : class where Self : UIControl {
    func respondeToControl() -> Bool
}

class CustomView: UIView {
    weak var delegate: CustomDelegate? {
        didSet{
            observeIfControlIsPressed()
        }
    }

    func observeIfControlIsPressed(){
        if delegate != nil {
            let keypath = delegate! as! UIControl

            keypath.addObserver(keypath,
                                forKeyPath: "backgroundColor",
                                options: [NSKeyValueObservingOptions.new, NSKeyValueObservingOptions.old],
                                context: nil)

        }
    }

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

class Foo : UIControl, CustomDelegate {
    func respondeToControl() -> Bool {
        return true
    }
}


class ViewController: UIViewController {

    let testBlock = UIView()

    override func viewDidLoad() {
        super.viewDidLoad()

        let view = CustomView()
        let button = Foo()

        view.delegate = button

        button.backgroundColor = UIColor.red
    }
}

Throws an exception:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '<Meh.Foo: 0x7fc977c1a0c0; baseClass = UIControl; frame = (0 0; 0 0); layer = <CALayer: 0x608000226480>>: An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.
Key path: backgroundColor

It means Foo is observing foo but did not implement the observeValueForKeyPath function..

Now why is that? Well.. it's because you did:

keypath.addObserver(keypath,  //keypath is observing itself..
                    forKeyPath: "backgroundColor",
                    options: [NSKeyValueObservingOptions.new, NSKeyValueObservingOptions.old],
                    context: nil)

where keypath is your delegate.. and is adding observer on itself..

It should be:

keypath.addObserver(self,  //self is observing keypath..
                        forKeyPath: "backgroundColor",
                        options: [NSKeyValueObservingOptions.new, NSKeyValueObservingOptions.old],
                        context: nil)

The first parameter of addObserver needs to be the class that wants to do the observing.

That change will cause it to print: background changed.. Don't forget to remove the observer later.

Brandon
  • 22,723
  • 11
  • 93
  • 186