その名もActionClosurable
どう使うか
以下のクラスおよびサブクラスがextensionで拡張されており、クロージャで処理を書くことができるようになっています。
UIControl
, UIButton
, UIGestureRecognizer
, UIBarButtonItem
and NSTimer
こんなふうに書けます。
let barButtonItem = UIBarButtonItem(title: "title", style: .Plain, closure: { _ in
print("barButtonItem title")
})
button.on(.TouchDown) {
$0.backgroundColor = UIColor.redColor()
}
button.on(.TouchUpOutside) {
$0.backgroundColor = UIColor.whiteColor()
}
button.onTap {
$0.enabled = false
print($0)
}
self.gr = UITapGestureRecognizer? = UITapGestureRecognizer()
view.addGestureRecognizer(gr!)
gr?.onGesture { [weak self] in
$0.removeTarget(nil, action: nil)
self?.gr = nil
}
var i = 5
self.timer = NSTimer.scheduledTimerWithTimeInterval(1, repeats: true) { [weak self] timer in
print("timer", i)
i -= 1
if i <= 0 {
timer.invalidate()
self?.timer = nil
}
}
なお、NSObjectを継承しているクラスであれば、好きなように拡張できます。めっちゃ簡単です。
どれだけ簡単かは、実際のソースを読んでいただければ分かるかと。
extension ActionClosurable where Self: UIButton {
public func onTap(closure: Self -> Void) {
registerClosure(closure) {
self.addTarget($0, action: $1, forControlEvents: .TouchUpInside)
}
}
}
extension ActionClosurable where Self: NSTimer {
public static func timerWithTimeInterval(ti: NSTimeInterval, repeats yesOrNo: Bool, closure: Self -> Void) -> Self {
return registerClosure(closure) {
let timer = Self.init(timeInterval: ti, target: $0, selector: $1, userInfo: nil, repeats: yesOrNo)
return timer
}
}
}
こんな感じ。ね、簡単でしょ?
使った技について
ActionClosurableはマイクロライブラリなので、短いソースです。コアのロジックは実質40行足らず。
長々と解説するものでもないんで、今回のこだわりポイントをざっくり箇条書きで挙げます。
- プロトコル指向
- 常に型を
Self
で表現することで、指定したクロージャでは引数に対して柔軟に型推論が掛かります。たとえば、UIButtonを継承したHogeButtonを作ったら、クロージャの型も自動的にHogeButton -> Void
になります。
- 常に型を
-
@noescape
- いいえ逃。
-
registerClosure
の中身で[weak self]
とか書かなくていいの、気持ちいいですね。
-
objc_setAssociatedObject
,objc_getAssociatedObject
- プロトコルの中でストアドプロパティを保持できるようになる裏技。
- actionを複数個設定しても動くように、ArrayやSetを噛ませて保持させようとしたら、どうも思ったタイミングでリリースされなくて結構ハマった。ここらへんの事情はそのうち単独の記事にしようかなと思います。