首先参考响应者链条 https://www.jianshu.com/p/c294d1bd963d
系统响应
- 首先 屏幕接收到点击后,IOKit会进行事件捕捉
- 而后通过 mach port传递给SpringBoad进程
- SpringBoard进程因接收到触摸事件,触发了主线程runloop的source1事件源的回调
- 如果此时在桌面,则交给桌面系统去消耗该事件
- 如果此时在app前台,则通过IPC(进程通信)传给app进程
APP响应
- 通过 mach port收到SpringBoad的触摸事件,主线程runloop被唤醒,触发source1回调
- source1回调触发soirce0回调,将触摸事件封成UIEvent事件
- source0将事件添加到UIApplication对象的队列中,开始寻找最佳响应者hit-testing
- 寻找到最佳响应者后,开始事件的响应在响应链中的传递和响应
- 触摸事件要么被某个响应对象捕获后释放,要么无法找到响应对象后释放
hit-Testing
- UIApplication 通过 调用UIWindow的 hitTest:withEvent: 传递给UIWindow,依次从后往前遍历子视图传递
- 过程
- 判断是否可交互
- 不允许交互:userInteractionEnabled = NO
- 隐藏:hidden = YES 如果父视图隐藏,那么子视图也会隐藏,隐藏的视图无法接收事件
- 透明度:alpha < 0.01 如果设置一个视图的透明度<0.01,会直接影响子视图的透明度。alpha:0.0~0.01为透明。
- 判断是否在视图中 if ([self pointInside:point withEvent:event] == NO) return nil;
- 判断是否在子视图中 CGPoint childP = [self convertPoint:point toView:childView];
UIView *fitView = [childView hitTest:childP withEvent:event]; - 重写父视图 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event 可以实现超出点击
- 判断是否可交互
事件响应
- 事件的最佳响应者是 hit-tested view,他可以独吞事件 也可以往后传递
- 不操作默认往下分发,通过nextResponder
- 在 touchesMoved: withEvent: 是否调用父类同名方法决定是否往下分发
事件优先级 UIResponder、UIGestureRecognizer、UIControl
UIResponder 和 UIGestureRecognizer
- 如果成功识别了 UIGestureRecognizer 则 优先进行 手势,而中止 hit-tested view的 UIResponder 过程。
- 触摸状态变化 的 begin 先发送到 手势 再 hit-tested
- 顺序为 手势begin —— hit-test begin—— 手势识别成功——手势end ——hit-test cancel
- 属性:cancelsTouchesInView 。默认YES,代表手势成功后,屏蔽响应链的响应
- 属性:delaysTouchesBegan 。默认NO,代表在手势识别期间不会调用 hit-test begin,move等
- 属性:delaysTouchesEnded 。默认YES,代表手势失败后,会延迟0.15s调用响应者的 touchesEnded:withEvent:,若NO,立即调用
UIControl
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(nullable UIEvent *)event;
- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(nullable UIEvent *)event;
- (void)endTrackingWithTouch:(nullable UITouch *)touch withEvent:(nullable UIEvent *)event;
- (void)cancelTrackingWithEvent:(nullable UIEvent *)event;
这几个方法是在 touch 系列里面调用的
- UIControl会阻止父视图上的手势识别器行为,也就是UIControl处理事件的优先级比UIGestureRecognizer高,但前提是相比于父视图上的手势识别器。
- UIControl比其父视图上的手势识别器具有更高的事件响应优先级。
- 准确地说只适用于系统提供的有默认action操作的UIControl,例如UIbutton、UISwitch等的单击
UIResponser
一:响应者链 UIResponser包括了各种Touch message 的处理,比如开始,移动,停止等等。常见的 UIResponser 有 UIView及子类,UIViController,APPDelegate,UIApplication等等。
回到响应链,响应链是由UIResponser组成的,那么是按照哪种规则形成的。
A: 程序启动 UIApplication会生成一个单例,并会关联一个APPDelegate。APPDelegate作为整个响应链的根建立起来,而``UIApplication会将自己与这个单例链接,即UIApplication的nextResponser(下一个事件处理者)为APPDelegate`。
B:创建UIWindow 程序启动后,任何的UIWindow被创建时,UIWindow内部都会把nextResponser设置为UIApplication单例。UIWindow初始化rootViewController,rootViewController的nextResponser会设置为UIWindow
C:UIViewController初始化 loadView, VC的view的nextResponser会被设置为VC.
D:addSubView addSubView操作过程中,如果子subView不是VC的View,那么subView的nextResponser会被设置为superView。如果是VC的View,那就是 subView -> subView.VC ->superView如果在中途,subView.VC被释放,就会变成subView.nextResponser = superView
网友评论