LoginSignup
2
6

More than 5 years have passed since last update.

UIButtonのAddTargetを書きたくない。

Last updated at Posted at 2018-03-09

やりたいこと


    let b = UIButton()
    b.onTap {
        print("on tap")
    }

実装方法はgithubでカンニングした。

実装

黒魔術を使う

objcのランタイム関数を使ってUIButtonのプロパティーにclosureを保持する構造体追加する

typealias TapClosure = ()->Swift.Void

class Tapable {

    static var key = "ON_TAP_KEY"
    private var base: UIButton

    init(_ base: UIButton) {
        self.base = base
    }

    var onTouchUpInside: TapClosure? {
        didSet {
            base.addTarget(self, action: #selector(touchUpInside), for: .touchUpInside)
        }
    }

    @objc private func touchUpInside(sender: AnyObject) {
        onTouchUpInside?()
    }
}


extension UIButton {

    func onTap(_ closure:  @escaping ()->Swift.Void) {
        tapable.onTouchUpInside = closure
    }

    private var tapable: Tapable {
        get {
            if let handler = objc_getAssociatedObject(self,  &Tapable.key) as? Tapable {
                return handler
            } else {
                self.tapable = Tapable(self)
                return self.tapable
            }
        }
        set {
            objc_setAssociatedObject(self, &Tapable.key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
}

NotificationCenterを使う

addObserver(forName:object:queue:using:)メソッドのブロックはコピーが生成される。
observerはどのタイミングでremoveすべきなんだろう・・・

typealias ActionClosure = ()->Swift.Void

extension Notification.Name {
    static let touchUpInsideName = Notification.Name("touchUpInsideName")
}

extension UIButton {

    func onTap(_ closure:  @escaping ActionClosure) {

        var observer: NSObjectProtocol?
        observer = NotificationCenter.default.addObserver(forName: .touchUpInsideName,
                                                          object: nil,
                                                          queue: nil) { _ in
            closure()
        }

        addTarget(self, action: #selector(touchUpInside), for: .touchUpInside)
    }

    @objc private func touchUpInside(sender: AnyObject) {

        NotificationCenter.default.post(name: .touchUpInsideName,
                                  object: self,
                                  userInfo: nil)
    }
}

とりあえずonTapメソッドを実装できれば個人的にはOKなのでクロージャーの引数もないしUIButton限定です。

参考

こちらからカンニングしました。

2
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
6