现在开发iOS的项目使用MVP/MVVM的已经越来越多了..某一UI控件的手势响应事件都不能在View层直接进行处理了而是需要传到P层去..比如给一个View添加一个单击手势一般会这么做.
一般用法
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let testView = UIView(frame: view.bounds)
testView.backgroundColor = UIColor.red
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tap(tap:)))
testView.addGestureRecognizer(tapGesture)
view.addSubview(testView)
}
@objc func tap(tap: UITapGestureRecognizer) {
vm.handleClickView(tap)
}
}
我们给一个view添加手势, 必须先声明一个Sel函数在View层, 然后再让vm去处理, 这样一旦代码多起来,响应事件多起来是很不好维护的,而且写起来多声明一个函数在不知道多少行的下面事件挺痛苦的事情. 在使 用MVP后更是加剧了痛苦指数..内聚性太低了...
封装一下的NiceGesture
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let testView = UIView(frame: view.bounds)
testView.backgroundColor = UIColor.red
testView.newTapGesture { (tap) in
tap.numberOfTapsRequired = 1
}.whenTaped { (tap) in
vm.handleClickView(tap)
}
testView.newLongpressGesture().whenBegan { (long) in
}.whenChanged { (long) in
}.whenEnded { (long) in
}
view.addSubview(testView)
}
}
省去了去声明一个距离创建手势N行的函数,, 在添加手势的地方直接将对应的响应方法传给了vm,Swfit本身闭包的简洁化可以让代码很有可读性而且不会看起来很乱.反而更加的直观, 函数化
这个手势的封装来自于" 没故事的卓同学" , 不过它的库已经两年没有更新了.我做了一些修改支持到Swift4.0并且修改了一些细节,tap与其它手势共用一个action类. .
[github]: https://github.com/Zoolofty/NewGesture
实现细节
因为每一个view都要有这个新API,只能为UIVIew添加扩展,或者让UIView遵循一个新协议(像Kingfisher).
extension UIView {
/// Pan
func newPanGesture(config: @escaping (UIPanGestureRecognizer) -> Void = {_ in }) -> NewGestureAction<UIPanGestureRecognizer> {
let pan = NewPanGesture(config: config)
addGestureRecognizer(pan)
return pan.gestureAction
}
extension中没有办法存储属性和函数,但必须有一个地方存储响应的Sel方法才行. 这里声明了一个继承于UIPanGestureRecoginze的NewPanGesture类(每一种手势一个, 因为每一个手势的配置方法不同).. 闭包参数中必须要有对这个手势的配置方法, 而且config有一个默认空实现,如果不需要可以省掉这个参数..
为了在初始化后可以用闭包注册响应方法, 返回值也要返回一个处理action的类.这里所有手势共用一个,实现中根据泛型来进行区别.
typealias PanGestureHandler = (UIPanGestureRecognizer) -> Void
class NewPanGesture: UIPanGestureRecognizer {
var gestureAction = NewGestureAction<UIPanGestureRecognizer>()
init(config: PanGestureHandler) {
super.init(target: gestureAction, action: #selector(gestureAction.gestureAction(gesture:)))
config(self)
}
}
在初始化后我们将注册的响应事件传给了GestureAction类, 将声明时对手势的配置通过config(self)设置给了自己...
事件处理类
class NewGestureAction<T: UIGestureRecognizer> {
typealias NewGestureHandler = (_ gestureRecognizer: T) -> Void
private var beginHandler: NewGestureHandler?
private var cancelledHandler: NewGestureHandler?
private var changeHandler: NewGestureHandler?
private var endedHandler: NewGestureHandler?
private var failedHandler: NewGestureHandler?
@objc func gestureAction(gesture: UIGestureRecognizer) {
switch gesture.state {
case .began:
beginHandler?(gesture as! T)
case .cancelled:
cancelledHandler?(gesture as! T)
case .changed:
changeHandler?(gesture as! T)
case .ended:
endedHandler?(gesture as! T)
case .failed:
failedHandler?(gesture as! T)
case .possible:
break
}
}
func whenBegan(handler: @escaping NewGestureHandler) -> NewGestureAction<T> {
beginHandler = handler
return self
}
func whenCancelled(handler: @escaping NewGestureHandler) -> NewGestureAction<T> {
cancelledHandler = handler
return self
}
func whenChanged(handler: @escaping NewGestureHandler) -> NewGestureAction<T> {
changeHandler = handler
return self
}
func whenEnded(handler: @escaping NewGestureHandler) -> NewGestureAction<T> {
endedHandler = handler
return self
}
func whenFailed(handler: @escaping NewGestureHandler) -> NewGestureAction<T> {
failedHandler = handler
return self
}
可以看出来
func gestureAction(gesture: UIGestureRecognizer)
里的方法就是我们声明时传入的响应handle方法,这也是我们能省下再去声明一个函数的原因..接下来就是根据不同情况调用不同的闭包就好了....
一开始我自己写的时候也是类似的思路,但我忘了存储响应事件的类在这个过程中是不能销毁的,所以继承与手势子类添加给这个view才可以..随便建一个类如果不与view建立联系那是不可以的.