Skip to content

Instantly share code, notes, and snippets.

@muizidn
Last active February 27, 2019 09:38
Show Gist options
  • Save muizidn/43fb5a907be160f92bb442f977e7893d to your computer and use it in GitHub Desktop.
Save muizidn/43fb5a907be160f92bb442f977e7893d to your computer and use it in GitHub Desktop.
An Action Director handle all your UIControl behavior. In beautiful and manageable way!
/// Target-Action helper.
final class Action<I>: NSObject {
private let _action: (I) -> Void
private let _input: I
private let identifier = UUID().uuidString
init(action: @escaping (I) -> Void, input: I) {
_action = action
_input = input
super.init()
}
@objc func action() {
_action(_input)
}
override func isEqual(_ object: Any?) -> Bool {
guard let action = object as? Action<I> else {
return false
}
return action.identifier == identifier
}
}
enum UIControlEvent {
case touchDown(UIControl)
case touchDownRepeat(UIControl)
case touchDragInside(UIControl)
case touchDragOutside(UIControl)
case touchDragEnter(UIControl)
case touchDragExit(UIControl)
case touchUpInside(UIControl)
case touchUpOutside(UIControl)
case touchCancel(UIControl)
case valueChanged(UIControl)
func add(target: Any?, action: Selector) {
switch self {
case let .touchDown(control):
control.addTarget(
target,
action: action,
for: .touchDown
)
case let .touchDownRepeat(control):
control.addTarget(
target,
action: action,
for: .touchDownRepeat
)
case let .touchDragInside(control):
control.addTarget(
target,
action: action,
for: .touchDragInside
)
case let .touchDragOutside(control):
control.addTarget(
target,
action: action,
for: .touchDragOutside
)
case let .touchDragEnter(control):
control.addTarget(
target,
action: action,
for: .touchDragEnter
)
case let .touchDragExit(control):
control.addTarget(
target,
action: action,
for: .touchDragExit
)
case let .touchUpInside(control):
control.addTarget(
target,
action: action,
for: .touchUpInside
)
case let .touchUpOutside(control):
control.addTarget(
target,
action: action,
for: .touchUpOutside
)
case let .touchCancel(control):
control.addTarget(
target,
action: action,
for: .touchCancel
)
case let .valueChanged(control):
control.addTarget(
target,
action: action,
for: .valueChanged
)
}
}
func remove(target: Any?, action: Selector?) {
switch self {
case let .touchDown(control):
control.removeTarget(
target,
action: action,
for: .touchDown
)
case let .touchDownRepeat(control):
control.removeTarget(
target,
action: action,
for: .touchDownRepeat
)
case let .touchDragInside(control):
control.removeTarget(
target,
action: action,
for: .touchDragInside
)
case let .touchDragOutside(control):
control.removeTarget(
target,
action: action,
for: .touchDragOutside
)
case let .touchDragEnter(control):
control.removeTarget(
target,
action: action,
for: .touchDragEnter
)
case let .touchDragExit(control):
control.removeTarget(
target,
action: action,
for: .touchDragExit
)
case let .touchUpInside(control):
control.removeTarget(
target,
action: action,
for: .touchUpInside
)
case let .touchUpOutside(control):
control.removeTarget(
target,
action: action,
for: .touchUpOutside
)
case let .touchCancel(control):
control.removeTarget(
target,
action: action,
for: .touchCancel
)
case let .valueChanged(control):
control.removeTarget(
target,
action: action,
for: .valueChanged
)
}
}
}
final class ActionsDirector {
private var actions: Set<NSObject> = [] {
didSet {
#if DEBUG
print("\(self).actions.count : \(actions.count)")
#endif
}
}
@discardableResult
func setupAction<I>(with event: UIControlEvent, input: I, callback: @escaping (I) -> Void) -> Action<I> {
let action = Action<I>(
action: callback,
input: input
)
defer {
event.add(
target: action,
action: #selector(action.action)
)
actions.insert(action)
}
return action
}
@discardableResult
func removeAction<I>(event: UIControlEvent, action: Action<I>) -> Bool {
defer {
event.remove(
target: action,
action: #selector(action.action)
)
}
return actions.remove(action) != nil
}
}
class Custom: UIView {
private var action: Action<Void>!
func registerOnTap(completion: @escaping () -> Void) {
action = Action<Void>.init(action: completion, input: ())
let tapGesture = UITapGestureRecognizer(target: action, action: #selector(action.action))
qrImageView.addGestureRecognizer(tapGesture)
}
}
@muizidn
Copy link
Author

muizidn commented Feb 21, 2019

Lets test!

class ViewController: UIViewController {
    let actionDirector = ActionsDirector()
    
    let control = UIControl(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
    var controlTouchUpInside: Action<(UITextField, Int)>! = nil
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let tf = UITextField(frame: CGRect(x: 100, y: 200, width: 100, height: 100))
        tf.backgroundColor = .yellow
        control.backgroundColor = .red
        let label = UILabel()
        label.text = "TAP"
        label.embed(in: control)
        view.addSubview(control)
        view.addSubview(tf)
        
        controlTouchUpInside = actionDirector.setupAction(
            with: .touchUpInside(control),
            input: (textField: tf, value: 0)
        ) { (input) in
            print("Input : \(input.0.text)")
            self.delete()
        }
    }
    
    func delete() {
        actionDirector.removeAction(
            event: .touchUpInside(control),
            action: controlTouchUpInside
        )
    }
}

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