前言:

我们点击屏幕的时候发生了什么?系统又是怎么找到对应的响应对象来响应的?

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具体内部做了什么

ios 事件传递按钮设置不可点击后事件传递怎么传递 ios事件响应和传递_ide

调用hittest去查询响应的view的时候,会做这些事情:

  1. 首先会查询当前view是否隐藏,是否可响应,alpha是否>0.01。
  2. 查询触摸点是否在view范围内部
  3. 倒叙遍历子视图
  4. 子视图调用hittest进行如步骤1同样的操作

若是最后返回某View,则对应的响应对象为该View;若是返回nil,则没有响应对象。

总结

  1. 点击一个UIView或产生一个触摸事件A,这个触摸事件A会被添加到由UIApplication管理的事件队列中。
  2. UIApplication会从事件对列中取出最前面的事件(此处假设为触摸事件A),把事件A传递给应用程序的主窗口(keyWindow)。
  3. 窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件。(至此,第一步已完成)

如果想让某个view不能接收事件(或者说,事件传递到某个view那里就断了),那么可以通过刚才提到的三种方式。比如,设置其userInteractionEnabled = NO;那么传递下来的事件就会由该view的父控件处理。
注意:如果设置父控件的透明度或者hidden,会直接影响到子控件的透明度和hidden。如果父控件的透明度为0或者hidden = YES,那么子控件也是不可见的!