やりたいこと
.swift
let b = UIButton()
b.onTap {
print("on tap")
}
実装方法はgithubでカンニングした。
実装
黒魔術を使う
objcのランタイム関数を使ってUIButtonのプロパティーにclosureを保持する構造体追加する
.swift
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すべきなんだろう・・・
.swift
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限定です。
参考
こちらからカンニングしました。