UIView触摸事件
1.响应者对象(UIResponder)
只有继承UIResponder的对象才能处理触摸事件,比如:
- UIApplication
- UIViewController
- UIView
UIResponder提供了以下方法处理触摸事件
1 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; |
我们可以重写这些方法来处理触摸事件
2.UITouch对象
上面处理方法里的touches装的就是UITouch对象。单一根手指触摸屏幕开始,一个UITouch对象就被创建,保存着该触摸时间、位置、点击次数(连续点击间隔得小于一定值)等信息。直到手指离开屏幕(时间间隔大于连续点击时间间隔)UITouch对象才被销毁。 一个手指一个UITouch对象。
3.事件的产生与First Responder的查找
当手指触摸屏幕时,就产生一个触摸事件(UIEvent),接着系统就寻找一个最前面(在手指下面,离手指最近)的UIResponder(First Responder)来处理该事件。这个寻找的过程网上很多叫事件传递。
具体寻找过程是这样的:
首先,系统将该UIEvent加入UIApplication的事件队列,UIApplication从队列中取出,并调用UIWindow的hitTest:withEvent
方法,该会返回可处理该事件的responder。hitTest:withEvent
逻辑如下:
判断 self.userInteractionEnabled && self.alpha>0.01 && !self.hidden && [self pointInside:point withEvent:event]
,为假则return nil
,该responder及其subView就没机会处理该事件。否则,逆序遍历self.subviews,并调用subview的hitTest:withEvent
,直到某个subview返回非nil对象,则返回该对象。没有subview返回非nil对象,则返回自己。
图解:hitTest:withEvent
可能实现
1 | - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { |
可见,一个view要接收UIEvent要满足四个条件:
这个View不是隐藏的:
1
self.hidden == NO
这个View允许用户交互:
1
self.userInteractionEnable == YES
这个View透明度大于0.01:
1
self.alpha > 0.01
这个View包含这个点:
1
[self pointInside:point withEvent:event] == YES
4.事件的响应
事件的响应就是调用上面说的UIResponder提供的方法如touchesBegan:withEvent:
处理相应事件
在上面我们找到了First Reponder,这时就形成了一条从First responder 到window的响应链。
tips
查找First Responder是从window到Frist Responder,而响应链则从First Reponder到window,正好相反。
默认的touchesBegan:withEvent:
只是把事件转发给响应链下一个responder。
注意:如果响应链中有view绑定了手势,那么这些手势会按响应链顺序得到该事件,进行匹配并处理,然后First Responder才得到对应事件,进行处理。