浅析iOS中事件的产生与响应过程
2016-10-03 本文已影响162人
BWLi420
想要学习事件的产生与响应过程首先要了解什么是响应者对象,什么是响应者链条。
响应者对象:继承了UIResponder的对象称之为响应者对象,也就是能处理事件的对象。
响应者链条:是由多个响应者对象连接起来的链条。
其次,并不是所有的控件都可以接收事件,在以下五种情况下控件不能接收事件:
-
不接收用户交互时不能够处理事件
userInteractionEnabled = NO;
注意:UIImageView的userInteractionEnabled默认就是NO
-
当一个控件隐藏的时候不能够接收事件
Hidden = YES
-
当一个控件为透明的时候也不能够接收事件
alpha <= 0.01;
-
父控件不能接收事件,那么子控件也不能接收事件
-
子控件超出父控件的大小
事件是怎样产生与传递的?
事件传递示例当用户点击屏幕后会产生一个触摸事件,系统会将该事件加入到一个由UIApplication管理的事件队列中。UIApplication会从事件队列中取出最前面的事件,并将事件传递给先发送事件给应用程序的主窗口以便处理,主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件。
怎么寻找处理事件最合适的视图 view ?
- 先判断自己是否能够接收触摸事件,如果能再继续往下判断;
- 再判断触摸的当前点在不在自己的身上;
- 如果在自己身上,它会从后往前遍历子控件,遍历出每一个子控件后,重复前面的两个步骤;
- 如果没有符合条件的子控件,那么它本身就是要寻找的最适合的View。
在寻找最合适的视图 view 中,需要使用到 hitTest
与 PointInside
两个方法。
hitTest 方法与 PointInside 方法
- hitTest 方法
- 作用:寻找最适合的View - 参数:当前手指所在的点,产生的事件 - 返回值:返回谁,谁就是最适合的View - 只要传递给一个控件时,就会调用这个控件的 hitTest 方法
- PointInside 方法
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;
}
```
#### 事件产生、传递与响应的完整过程
1. 当用户点击屏幕后会产生一个触摸事件,系统会将该事件加入到一个由UIApplication管理的事件队列中;
2. UIApplication会从事件队列中取出最前面的事件,并将事件传递给先发送事件给应用程序的主窗口以便处理;
3. 主窗口会调用 hitTest 方法寻找最适合的视图控件,找到后就会调用视图控件的 touches 方法来做具体的事情;
4. 当调用 touches 方法,它的默认做法会将事件顺着响应者链条往上传递;
5. 传递给上一个响应者,接着就会调用上一个响应者的touches方法。
![响应者链条示意图](http:https://img.haomeiwen.com/i2997426/d5b481373f9f4834.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
#### 如何寻找上一个响应者?
1. 如果当前的View是控制器的View,那么控制器就是上一个响应者
2. 如果当前的View不是控制器的View,那么它的父控件就是上一个响应者
3. 在视图层次结构的最顶级视图,如果也不能处理收到的事件或消息,则其将事件或消息传递给window对象进行处理
4. 如果window对象也不处理,则其将事件或消息传递给UIApplication对象
5. 如果UIApplication也不能处理该事件或消息,则将其丢弃