前言:
我们点击屏幕的时候发生了什么?系统又是怎么找到对应的响应对象来响应的?
1.事件
当我们点击屏幕的时候,会产生一个事件。其实就是UIEvent对象。
@property(nonatomic,readonly) UIEventType type;//类型
@property(nonatomic,readonly) UIEventSubtype subtype;
@property(nonatomic,readonly) NSTimeInterval timestamp;
而UIEvent又分为三种类型:
typedef NS_ENUM(NSInteger, UIEventType) {
UIEventTypeTouches, //触摸
UIEventTypeMotion, //加速计
UIEventTypeRemoteControl, //远程控制
UIEventTypePresses NS_ENUM_AVAILABLE_IOS(9_0),
};
2.事件传递
关于事件响应,这里使用触摸事件来进行归纳。在事件响应和传递的过程中有两个方法是经常提到的,大多数人也了解这两个方法。
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event; // recursively calls -pointInside:withEvent:. point is in the receiver's coordinate system
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event; // default returns YES if point is in bounds
hittest方法用于判断当前是否是响应的视图,而pointinside用于判断触摸的位置是否在视图范围内部。
事件传递的流程
- 点击屏幕,产生UIEvent对象
- 系统将对象添加到UIApplication的处理队列中,先添加的先处理
- UIApplication通过runloop将事件分发到UIWindow中进行处理,通常是keywindow
- UIWindow调用hittest方法去查找对应的view
hitTest具体内部做了什么
调用hittest去查询响应的view的时候,会做这些事情:
- 首先会查询当前view是否隐藏,是否可响应,alpha是否>0.01。
- 查询触摸点是否在view范围内部
- 倒叙遍历子视图
- 子视图调用hittest进行如步骤1同样的操作
若是最后返回某View,则对应的响应对象为该View;若是返回nil,则没有响应对象。
总结
- 点击一个UIView或产生一个触摸事件A,这个触摸事件A会被添加到由UIApplication管理的事件队列中。
- UIApplication会从事件对列中取出最前面的事件(此处假设为触摸事件A),把事件A传递给应用程序的主窗口(keyWindow)。
- 窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件。(至此,第一步已完成)
如果想让某个view不能接收事件(或者说,事件传递到某个view那里就断了),那么可以通过刚才提到的三种方式。比如,设置其userInteractionEnabled = NO;那么传递下来的事件就会由该view的父控件处理。
注意:如果设置父控件的透明度或者hidden,会直接影响到子控件的透明度和hidden。如果父控件的透明度为0或者hidden = YES,那么子控件也是不可见的!