Skip to content

Instantly share code, notes, and snippets.

@wildthink
Last active December 5, 2019 21:08
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save wildthink/677308084ab364044c76 to your computer and use it in GitHub Desktop.
Save wildthink/677308084ab364044c76 to your computer and use it in GitHub Desktop.
ActionTrampoline - Use Swift blocks for your Control actions
//
// ActionTrampoline.swift
// Swift 3
//
// Created by Jason Jobe on 3/17/16.
// Copyright © 2016 WildThink. All rights reserved.
//
// https://gist.githubusercontent.com/wildthink/677308084ab364044c76/raw/f713efca9ec6ca9b56c4405bd82ae33b1db98ec7/ActionTrampoline.swift
//
// Kudos (again) to Mike Ash!
// https://www.mikeash.com/pyblog/friday-qa-2015-12-25-swifty-targetaction.html
//
import Foundation
import Foundation
#if os(iOS)
import UIKit
protocol UIControlActionFunctionProtocol {}
class ActionTrampoline<T>: NSObject
{
var action: (T) -> Void
init(action: @escaping (T) -> Void) {
self.action = action
}
@objc func performAction(sender: UIControl) {
action(sender as! T)
}
}
let NSControlActionFunctionProtocolAssociatedObjectKey = UnsafeMutablePointer<Int8>.allocate(capacity: 1)
extension UIControlActionFunctionProtocol where Self: UIControl
{
func addAction(events: UIControlEvents, _ action: @escaping (Self) -> Void) {
let trampoline = ActionTrampoline(action: action)
let call = #selector(trampoline.performAction(sender:))
self.addTarget(trampoline, action: call, for: events)
objc_setAssociatedObject(self, NSControlActionFunctionProtocolAssociatedObjectKey, trampoline, .OBJC_ASSOCIATION_RETAIN)
}
func removeActionBlock() {
objc_setAssociatedObject(self, NSControlActionFunctionProtocolAssociatedObjectKey, nil,
.OBJC_ASSOCIATION_RETAIN)
}
func setup(setup: (Self) -> Void) -> Self {
setup(self)
return self
}
}
extension UIControl: UIControlActionFunctionProtocol {}
#else
import Cocoa
protocol NSControlActionFunctionProtocol {}
class ActionTrampoline<T>: NSObject
{
var action: (T) -> Void
init(action: @escaping (T) -> Void) {
self.action = action
}
@objc func performAction(sender: NSControl) {
action(sender as! T)
}
}
let NSControlActionFunctionProtocolAssociatedObjectKey = UnsafeMutablePointer<Int8>.allocate(capacity: 1)
extension NSControlActionFunctionProtocol where Self: NSControl
{
var actionBlock: ((Self) -> Void)? {
set {
guard let action = newValue else {
objc_setAssociatedObject(self, NSControlActionFunctionProtocolAssociatedObjectKey, nil,
.OBJC_ASSOCIATION_RETAIN)
return
}
let trampoline = ActionTrampoline(action: action)
self.target = trampoline
self.action = #selector(trampoline.performAction(sender:))
objc_setAssociatedObject(self, NSControlActionFunctionProtocolAssociatedObjectKey, trampoline, .OBJC_ASSOCIATION_RETAIN)
}
get {
guard let trampoline: ActionTrampoline =
objc_getAssociatedObject(self, NSControlActionFunctionProtocolAssociatedObjectKey) as? ActionTrampoline<Self> else { return nil }
return trampoline.action
}
}
func setup(setup: (Self) -> Void) -> Self {
setup(self)
return self
}
}
extension NSControl: NSControlActionFunctionProtocol {}
#endif
// Demo
#if os(iOS)
var button = UIButton(frame: CGRect(x: 0, y: 0, width: 64, height: 32))
button.addAction(events: .allEditingEvents) {
Swift.print ($0.title(for: .highlighted) ?? "uhoh")
}
button.sendActions(for: .allEditingEvents)
#else
var button = NSButton(frame: NSRect(x: 0, y: 0, width: 64, height: 32))
button.setAction() {
Swift.print ($0.title)
}
button.actionBlock = {
Swift.print ($0.title)
}
let button2 = NSButton(frame: NSRect(x: 0, y: 0, width: 64, height: 32)).setup { me in
me.setAction () {
Swift.print ($0.title)
}
}
print (button.actionBlock)
button.performClick(nil)
button.actionBlock?(button)
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment