名词与基本概念
-
响应者:
在iOS中,响应者为能响应事件的UIResponder子类对象,如UIButton、UIView等。 -
响应链:
响应链是由链接在一起的响应者(UIResponse子类)组成的。默认情况下,响应链是由第一响应者,到application对象以及中间所有响应者一起组成的。 -
事件传递:
获得响应链后,将事件由第一响应者往application传递的过程即为事件传递。
简单总结
事件的响应流程如下
1.首先通过 hitTest:withEvent: 确定第一响应者,以及相应的响应链(UIApplication -> UIWindow -> ... -> 响应者)
2.判断第一响应者能否响应事件,如果第一响应者能进行响应则事件在响应链中的传递终止。如果第一响应者不能响应则将事件传递给 nextResponder也就是通常的superview进行事件响应
3.如果事件继续上报至UIWindow并且无法响应,它将会把事件继续上报给UIApplication
4.如果事件继续上报至UIApplication并且也无法响应,它将会将事件上报给其Delegate
5.如果最终事件依旧未被响应则会被系统抛弃
一些实现
寻找响应者:UIApplication -> UIWindow -> ... -> 响应者
// 寻找响应者
- (UIView *)hitTest:(CGPoint)point
withEvent:(UIEvent *)event;
// 判定是否在view内
- (BOOL)pointInside:(CGPoint)point
withEvent:(UIEvent *)event;
// hit test 假代码
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
if (self.hidden || !self.userInteractionEnabled || self.alpha < 0.01 || ![self pointInside:point withEvent:event] || ![self _isAnimatedUserInteractionEnabled]) {
return nil;
} else {
for (UIView *subview in [self.subviews reverseObjectEnumerator]) {
UIView *hitView = [subview hitTest:[subview convertPoint:point fromView:self] withEvent:event];
if (hitView) {
return hitView;
}
}
return self;
}
}
// UIApplication 派发event的两个方法,派发给合适的响应者。 (对于响应链来说是他的windows)
- (void)sendEvent:(UIEvent *)event;
- (BOOL)sendAction:(SEL)action
to:(id)target
from:(id)sender
forEvent:(UIEvent *)event;
// Dispatches the specified event to its views. UIWindow派发事件给他的 views
- (void)sendEvent:(UIEvent *)event;
事件传递: 响应者 -> super view -> ... -> UIApplication
看谁能实现
几个要点
1. 手势比响应链拥有更高的优先级
2. 视图不响应检查要点
hidden = YES 视图被隐藏
userInteractionEnabled = NO 不接受响应事件
alpha <= 0.01,透明视图不接收响应事件
子视图超出父视图范围
需响应视图被其他视图盖住
是否重写了其父视图以及自身的hitTest方法
是否重写了其父视图以及自身的pointInside方法