Skip to content

Instantly share code, notes, and snippets.

@radutzan
Last active December 13, 2018 19:43
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save radutzan/893d3dc5f44ea79aff232db16988a47c to your computer and use it in GitHub Desktop.
Save radutzan/893d3dc5f44ea79aff232db16988a47c to your computer and use it in GitHub Desktop.
A way to declare a UIButton's tap, touch down, and "touch lift" actions through closure properties. See comments for more info.
typealias ButtonAction = (button: UIButton) -> Void
class ButtonActionWrapper: NSObject {
var action: ButtonAction
init(action: ButtonAction) {
self.action = action
}
}
private struct ButtonActionKeys {
static var tap = "this is the tap key"
static var touchDown = "this is the touch down key"
static var touchLift = "this is the touch lift key"
}
extension UIButton {
var tapAction: ButtonAction? {
get {
if let wrapper = objc_getAssociatedObject(self, &ButtonActionKeys.tap) as? ButtonActionWrapper {
return wrapper.action
}
return nil
}
set {
if let anAction = newValue {
addTarget(self, action: #selector(runTapAction), forControlEvents: .TouchUpInside)
objc_setAssociatedObject(self, &ButtonActionKeys.tap, ButtonActionWrapper(action: anAction), objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
} else {
removeTarget(self, action: #selector(runTapAction), forControlEvents: .TouchUpInside)
}
}
}
var touchDownAction: ButtonAction? {
get {
if let wrapper = objc_getAssociatedObject(self, &ButtonActionKeys.touchDown) as? ButtonActionWrapper {
return wrapper.action
}
return nil
}
set {
if let anAction = newValue {
addTarget(self, action: #selector(runTouchDownAction), forControlEvents: .TouchDown)
objc_setAssociatedObject(self, &ButtonActionKeys.touchDown, ButtonActionWrapper(action: anAction), objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
} else {
removeTarget(self, action: #selector(runTouchDownAction), forControlEvents: .TouchDown)
}
}
}
var touchLiftAction: ButtonAction? {
get {
if let wrapper = objc_getAssociatedObject(self, &ButtonActionKeys.touchLift) as? ButtonActionWrapper {
return wrapper.action
}
return nil
}
set {
if let anAction = newValue {
addTarget(self, action: #selector(runTouchLiftAction), forControlEvents: .TouchCancel)
objc_setAssociatedObject(self, &ButtonActionKeys.touchLift, ButtonActionWrapper(action: anAction), objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
} else {
removeTarget(self, action: #selector(runTouchLiftAction), forControlEvents: .TouchCancel)
}
}
}
func runTapAction() {
touchLiftAction?(button: self)
tapAction?(button: self)
}
func runTouchDownAction() {
touchDownAction?(button: self)
}
func runTouchLiftAction() {
touchLiftAction?(button: self)
}
}
@radutzan
Copy link
Author

radutzan commented Aug 12, 2016

An evolution of the target-action model for the Swift era. Target is always self, since other objects can easily be called from inside the closure. This allows for faster prototyping, potentially cleaner class interfaces with less "buttonTapped" methods, and a pattern closer to Swift property observers.

  • tapAction is equivalent to an action called on .TouchUpInside
  • touchDownAction is equivalent to an action called on .TouchDown
  • touchLiftAction is equivalent to an action called on either .TouchUpInside or .TouchCancel

Example call site usage:

yourButton.tapAction = { button in
    // do your thing
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment