以Closure的方式优雅地为UIControl addTarget
习惯了ES6的闭包,回到Swift下发现为UIButton手动添加一个点击事件,实在是太ugly,你得先定义一个方法(selector),然后用OC时代的方式去call,最不方便的是传参数,尤其是你想使用本地变量的时候。
研究了一下,实现了以Closure的方式addTarget,方便了不少。
首先添加一个UIControl的extension,这样包括UIButton在内的所有子类都能使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import UIKit
class ClosureSleeve { let closure: ()->() init (_ closure: @escaping ()->()) { self.closure = closure } @objc func invoke () { closure() } }
extension UIControl { func addAction(for controlEvents: UIControlEvents, _ closure: @escaping ()->()) { let sleeve = ClosureSleeve(closure) addTarget(sleeve, action: #selector(ClosureSleeve.invoke), for: controlEvents) objc_setAssociatedObject(self, String(format: "[%d]", arc4random()), sleeve, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN) } func removeActions() { objc_removeAssociatedObjects(self) } }
|
如何使用:
1 2 3
| button.addAction(for: .touchUpInside) { print("Hello, Closure!") }
|
避免循环引用:
1 2 3
| self.button.addAction(for: .touchUpInside) { [weak self] in self?.doStuff() }
|
注意
需要注意的一点是,如果是在UITableCell等控件中使用,因为控件的复用机制,稍不注意会addAction多次,建议在prepareForReuse
方法中removeActions
:
1 2 3
| override func prepareForReuse() { button.removeActions() }
|