想要学习事件的产生与响应过程首先要了解什么是响应者对象,什么是响应者链条。
响应者对象:继承了UIResponder的对象称之为响应者对象,也就是能处理事件的对象。
响应者链条:是由多个响应者对象连接起来的链条。
其次,并不是所有的控件都可以接收事件,在以下五种情况下控件不能接收事件:
-
不接收用户交互时不能够处理事件
userInteractionEnabled = NO;
注意:UIImageView的userInteractionEnabled默认就是NO
-
当一个控件隐藏的时候不能够接收事件
Hidden = YES
-
当一个控件为透明的时候也不能够接收事件
alpha <= 0.01;
父控件不能接收事件,那么子控件也不能接收事件
子控件超出父控件的大小
事件是怎样产生与传递的?
当用户点击屏幕后会产生一个触摸事件,系统会将该事件加入到一个由UIApplication管理的事件队列中。UIApplication会从事件队列中取出最前面的事件,并将事件传递给先发送事件给应用程序的主窗口以便处理,主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件。
怎么寻找处理事件最合适的视图 view ?
- 先判断自己是否能够接收触摸事件,如果能再继续往下判断;
- 再判断触摸的当前点在不在自己的身上;
- 如果在自己身上,它会从后往前遍历子控件,遍历出每一个子控件后,重复前面的两个步骤;
- 如果没有符合条件的子控件,那么它本身就是要寻找的最适合的View。
在寻找最合适的视图 view 中,需要使用到 hitTest
与 PointInside
两个方法。
hitTest 方法与 PointInside 方法
-
hitTest 方法
-
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
- 作用:寻找最适合的View
- 参数:当前手指所在的点,产生的事件
- 返回值:返回谁,谁就是最适合的View
- 只要传递给一个控件时,就会调用这个控件的 hitTest 方法
-
-
PointInside 方法
-
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{ return YES; }
- 作用:判断point在不在方法调用者上
- point:必须是方法调用者的坐标系
- hitTest方法底层会调用这个方法,判断点在不在控件上
-
-
hitTest 方法的底层实现
① 判断当前控件能不能接收事件
if(self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) { return nil; }
② 判断触摸点在不在当前的控件上
if(![self pointInside:point withEvent:event]) { return nil; }
③ 从后往前遍历自己的子控件
int count = (int)self.subviews.count; for (int i = count - 1; i >= 0;i-- ) { UIView *childV = self.subviews[i]; //把当前坐标系上的点转换成子控件坐标系上的点 CGPoint childP = [self convertPoint:point toView:childV]; //判断自己的子控件是不是最适合的View UIView *fitView = [childV hitTest:childP withEvent:event]; //如果子控件是最适合的View,直接返回 if (fitView) { return fitView; } //否则自己就是最适合的View return self; }
事件产生、传递与响应的完整过程
- 当用户点击屏幕后会产生一个触摸事件,系统会将该事件加入到一个由UIApplication管理的事件队列中;
- UIApplication会从事件队列中取出最前面的事件,并将事件传递给先发送事件给应用程序的主窗口以便处理;
- 主窗口会调用 hitTest 方法寻找最适合的视图控件,找到后就会调用视图控件的 touches 方法来做具体的事情;
- 当调用 touches 方法,它的默认做法会将事件顺着响应者链条往上传递;
-
传递给上一个响应者,接着就会调用上一个响应者的touches方法。
如何寻找上一个响应者?
- 如果当前的View是控制器的View,那么控制器就是上一个响应者
- 如果当前的View不是控制器的View,那么它的父控件就是上一个响应者
- 在视图层次结构的最顶级视图,如果也不能处理收到的事件或消息,则其将事件或消息传递给window对象进行处理
- 如果window对象也不处理,则其将事件或消息传递给UIApplication对象
- 如果UIApplication也不能处理该事件或消息,则将其丢弃