0

Is it possible to subclass UIEvent to embed some additional information? I tried but keep getting exceptions and can't find anything about subclassing UIEvent.

class CustomEvent: UIEvent {
      let payload: CustomData
      override init(payload: CustomData) {
            super.init()
            self.payload = payload
      }
}


017-03-30 08:51:17.504497 StealingTouches[1041:778674] -[StealingTouches.TouchEvent _firstTouchForView:]: unrecognized selector sent to instance 0x170053950
2017-03-30 08:51:17.505470 StealingTouches[1041:778674] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[StealingTouches.TouchEvent _firstTouchForView:]: unrecognized selector sent to instance 0x170053950'
*** First throw call stack:
(0x18bf991b8 0x18a9d055c 0x18bfa0268 0x18bf9d270 0x18be9680c 0x191e8494c 0x100026278 0x100026550 0x100026fc4 0x100027214 0x191e7be98 0x191e77328 0x191e47da0 0x19263175c 0x19262b130 0x18bf46b5c 0x18bf464a4 0x18bf440a4 0x18be722b8 0x18d926198 0x191eb27fc 0x191ead534 0x100029418 0x18ae555b8)
libc++abi.dylib: terminating with uncaught exception of type NSException

I have a collection of views nested 3 levels deep inside superviews. I want the deeply nested view to add some custom data to the event, and forward this data up to the outermost view.

See image. The green views should handle the event, do the appropriate calculations, save the data into the event, and forward the data up to the red view. Only the green views respond to events, but only the red view knows what to do with that event.

enter image description here

Green view handles the touches...

class GreenView: UIControl {
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)

        //do some complicated calculation then save in custom event
        let customEvent = CustomEvent.init(payload: calculations)

        if let nextRespoonder = self.next {
            nextRespoonder.touchesBegan(touches, with: customEvent)
        } 
    }
}

Which then gets forwarded up to the yellow view...

 class YellowView: UIControl {
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        if let nextRespoonder = self.next {
            nextRespoonder.touchesBegan(touches, with: event)
        } 
    }
}

And finally the red view can extract the event payload and do what it needs to do...

 class RedView: UIControl {
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        if let customEvent = event as? CustomEvent {
               let payload = customEvent.payload
               //do something with the payload
        }

    }
}

Another option is to store the payload data locally in the green view, and then the only thing the red view needs to do is identify which green view initiated the event. This is fairly simple to do with hit test, but I have over a hundred of these green views and it can get fairly complicated to figure out which one is which based on hit test alone as sometimes green views overlap each other.

MH175
  • 2,234
  • 1
  • 19
  • 35

1 Answers1

0

I learned that this pattern is the wrong way to handle this. The better thing to do is to use delegation. Assign redView as the delegate for greenView and just pass info up.

protocol GreenViewDelegate {
     func onTouchesBegan(greenView: GreenView)
}

class GreenView: UIControl {

    var delegate: GreenViewDelegate?

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        self.delegate?.onTouchesBegan(greenView: self)
    }


}

class RedView: UIControl, GreenViewDelegate {

    init() {
        greenView.delegate = self
    } 

    func onTochesBegan(greenView: GreenView) {
         //extract whatever data you want
    }

}
MH175
  • 2,234
  • 1
  • 19
  • 35