这里的事件分发机制中的事件是触摸事件,ios中处理触摸事件分为两种,其一便是使用手势识别器来跟踪触摸 其二是UIView中系统写好的处理事件机制,也是本文重点介绍的事件分发机制
对于第一种,事件传递和响应都比较简单,手势识别器封装了处理和解释该视图的传入事件所需的所有逻辑,并将它们与已知模式相匹配,没有寻找view的过程。检测到匹配时,直接通知添加的target执行响应方法。对view对象,如果添加了手势,两种方式都会执行
![](https://img.haomeiwen.com/i1391187/ee24b6ec4c2251ef.png)
执行该方式的方法调用
![](https://img.haomeiwen.com/i1391187/b9807bf5a256b31f.png)
第二种由于view的层层嵌套,不能直接找到hitTestView, 于是便有了事件分发机制
这个事件到底谁是最佳的响应人选? 换个说法就是怎么找到最佳的人选
复现hitTest: withEvent:方法源码
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (!self.isUserInteractionEnabled || self.isHidden || self.alpha <= 0.01) {
return nil;
}
if ([self pointInside:point withEvent:event]) {
for (UIView *subview in [self.subviews reverseObjectEnumerator]) {
CGPoint convertedPoint = [subview convertPoint:point fromView:self];
UIView *hitTestView = [subview hitTest:convertedPoint withEvent:event];
if (hitTestView) {
return hitTestView;
}
}
return self;
}
return nil;
}
熟读该源码并背诵 ,以下均为对该源码的解释
1.首先看下事件传递用到的主要方法
参数point均为相对于接受者自身坐标系的坐标,上面源码中通过convertPoint:fromView:转换坐标系
/*
通过递归调用 -pointInside:withEvent:,寻找view栈中符合条件的最深的view
*/
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;
// default returns YES if point is in bounds(默认相对于自身坐标系)
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;
2.成为hittestview的条件
从源码中的几个判断,可以看出条件为:
- isUserInteractionEnabled = yes
- isHidden = no
- alpha > 0.01
- [self pointInside:point withEvent:event] 为yes
3.调用栈
![](https://img.haomeiwen.com/i1391187/3ecb871e4baed5ac.png)
实际调用过程中同一个view的hitTest:withEvent:方法可能会执行多次,苹果开发者解释为:系统可能会调整touch point,所以会多次调用hit test
从方式一和二的调用栈中可以总结出事件传递的顺序
当用户与iPhone的触摸屏产生互动时,硬件就会探测到物理接触并且通知操作系统。接着操作系统就会创建相应的事件并且将其传递给当前正在运行的应用程序的事件队列。(从栈中可以看到用到了好多runloop的知识,先不管了)。然后调用了UIApplation和window的sendEvent方法,最后根据是否有符合的手势识别器来执行后续寻找view的步骤。
方式一:
UIApplication -> UIWindow -> Gesture -> target执行方法
方式二:
UIApplication -> UIWindow -> Root View -> ··· -> subview
网友评论