从用户触摸屏幕到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的类。
应用
根据事件的传递和响应机制,我们可以在事件传递和响应过程中做操作,以改变原有的事件响应。
- 事件透传
- 改变响应区域
网友评论