触摸事件的传递和响应机制

从用户触摸屏幕到app的响应过程。

过程

事件的产生及分发

  • 点击屏幕会产生一个触摸事件。
  • 主线程的runloop会接收到该事件并将其存放到消息队列中。
  • UIApplication 会从消息队列中取事件并将事件分发给window。

找寻合适的视图

在寻找合适视图的过程中,主要会用到UIView的两个方法:- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;以及- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;
hitTest方法中会对视图进行一定的判断:比如 userInteractionEnabled 、isHidden 、alpha 是否>=0.01,如果视图可交互,非隐藏,透明度比0.01大,就会调用pointInside方法判定事件的触发点是否在视图区域内,否则hitTest会返回nil。

  • 事件被分发到window,事件就开始在视图树中传递,寻找合适的视图。
  • 视图会调用hitTest方法,在hitTest方法中会调用pointInside方法,若pointInside返回NO,hitTest返回nil。如果pointInside返回YES,则判断该视图是否有子视图。
  • 如果没有子视图,该视图的hitTest会返回该视图。如果有子视图,会倒序遍历子视图,子视图会调用hitTest。
  • 如果所有的子视图的hitTest都返回nil,则该视图的hitTest返回该视图。
  • 若第一次有hitTest返回非空的视图对象。则递归返回该视图,该视图就是最合适的视图。
  • 如果最终没有找到合适的视图,window会将事件返还给UIApplication,系统会忽略掉此事件。

事件的响应

  • 找到合适的响应者后,事件的响应链已确定。
  • 首先判断响应者是否能响应此事件。如果可以响应,则响应事件。
  • 如果不能响应,则会沿着响应链来寻找响应者。如果最终没有找到能响应的响应者,则系统会忽略掉此事件。

响应链的构建情况:

  • view:如果view是被UIViewController对象管理,view的下一个响应者就是UIViewController;如果不是被UIViewController对象管理的,下一个响应者就是superview。
  • UIViewController:同返回它管理的view的superview。
  • UIWindow:返回application对象。
  • UIApplication:返回给应用委托AppDelegate。

注意点儿

一次触摸事件,触发两次hitTest:

  • uiview(geometry) _hitTest:withEvent:windowServerHitTestWindow:
  • __updateTouchesWithDigitizerEventAndDeterminelfShouldSend_block_invoke

UIControl 和 UIButton ,会影响事件的响应。
如果能响应事件,则响应事件。若不能响应,会截断事件的响应。

UIGestureRecognizer 和 UITextField ,会影响事件响应。
若能响应事件,会将触摸取消。若不能响应,会拦截事件的响应。

UIGestureRecognizer是一个基于NSObject的类,它能影响事件的响应,是因为它会被添加到一个View上。

UITextField ,UIButton 是基于UIControl的类, UIControl 是基于UIview的。
UIApplication 和 UIViewController 是基于 UIResponder的类。

应用

根据事件的传递和响应机制,我们可以在事件传递和响应过程中做操作,以改变原有的事件响应。

  • 事件透传
  • 改变响应区域
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容