Create a gist now

Instantly share code, notes, and snippets.

Embed
Swift3 UIControl extension for adding block event listeners. Adapted from: https://stackoverflow.com/a/44917661/44639
import Foundation
import UIKit
extension UIControl {
func listen(_ action: @escaping () -> (), for controlEvents: UIControlEvents) -> AnyObject {
let sleeve = ClosureSleeve(attachTo: self, closure: action, controlEvents: controlEvents)
addTarget(sleeve, action: #selector(ClosureSleeve.invoke), for: controlEvents)
return sleeve
}
func listenOnce(_ action: @escaping () -> (), for controlEvents: UIControlEvents) {
let sleeve = ClosureSleeve(attachTo: self, closure: action, controlEvents: controlEvents)
addTarget(sleeve, action: #selector(ClosureSleeve.invokeOnce), for: controlEvents)
}
func unlisten(sleeve: AnyObject) {
guard let sleeve = sleeve as? ClosureSleeve else { return }
self.removeTarget(sleeve, action: #selector(ClosureSleeve.invoke), for: sleeve.controlEvents)
}
}
private class ClosureSleeve {
let closure: () -> ()
let controlEvents:UIControlEvents
let attachedTo: AnyObject
init(attachTo: AnyObject, closure: @escaping () -> (), controlEvents:UIControlEvents) {
self.attachedTo = attachTo
self.closure = closure
self.controlEvents = controlEvents
objc_setAssociatedObject(attachTo, "[\(arc4random())]", self, .OBJC_ASSOCIATION_RETAIN)
}
@objc func invoke() {
closure()
}
@objc func invokeOnce() {
closure()
attachedTo.unlisten(sleeve: self)
}
}
// Register listener, keep the reference to unregister the listener
let listener = button.listenOnce({
print("I will say this every time you tap the button")
}, for: [.touchUpInside])
// … later …
button.unlisten(listener)
// Listen once for the control events, automatically unlisten when the block is performed
button.listenOnce({
print("I will only say this once")
}, for: [.touchUpInside, .touchDragExit])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment