0x00 事件
iOS 中的事件需要继承 UIResponder
, 凡是响应者都会以下方法:
// 触摸事件
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
// 传感器事件(摇一摇的实现)
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event;
// 远程控制事件
- (void)remoteControlReceivedWithEvent:(UIEvent *)event;
0x01 传递中寻找合适的 View
主要通过以下方法寻找合适的 View:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;
其中, hitTest
方法是从 Window 开始查找合适的 View
- 判断当前控件能否接收事件
- 判断点在不在当前控件
- 倒序遍历当前控件的子控件
- 转换子控件坐标, 并调用
hitTest
- 如果找到合适的就直接返回, 找不到就返回自己
而 pointInside
, 是判断点是否在当前控件内, 并且是否能响应事件
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *fitView = nil;
// 1. 判断当前控件能否接收事件
if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return fitView;
// 2. 判断点在不在当前控件
if ([self pointInside:point withEvent:event] == NO) return fitView;
// 3. 倒序遍历当前控件的子控件
NSUInteger count = self.subviews.count;
for (NSUInteger i = count - 1; i >= 0; --i) {
UIView *childView = self.subviews[i];
// 4. 转换子控件坐标, 并调用 hitTest
CGPoint childPoint = [self convertPoint:point toView:childView];
fitView = [childView hitTest:childPoint withEvent:event];
// 5. 寻找到最合适的 view
if (fitView) {
return fitView;
}
}
// 6. 循环结束, 表示没有比自己更合适的 view
return self;
}
0x02 响应链
-
UIView
如果 view 是 ViewController 的根 view,那么下一个响应者是 ViewController ,否则是 SuperView -
UIViewController
如果 ViewController 的 view 是 window 的根 view, 那么下一个响应者是window
如果 ViewController 是另一个 ViewController 模态推出的, 那么下一个响应者是另一个 ViewController
如果 ViewController 的 view 被 add 到另一个 ViewController 的根 view 上, 那么下一个响应者是另一个 ViewController 的根 view -
UIWindow
UIWindow 的下一个响应者是 UIApplication -
UIApplication
通常 UIApplication 是响应者链的顶端(如果 AppDelegate 也继承了UIResponder,事件还会继续传给 AppDelegate)
0x03 Final
产生触摸事件 ➡️
UIApplication 事件队列 ➡️
[UIWindow hitTest:withEvent:] ➡️
返回更合适的 view ➡️
[子控件 hitTest:withEvent:] ➡️
返回最合适的 view ➡️
最合适的 view 调用 touches 方法 ➡️
判断是否实现 touches 方法 ➡️
没有实现默认会将事件传递给上一个响应者 ➡️
找到上一个响应者 ➡️
找不到方法作废
找最合适的 View 是从根往枝叶上去查找, 最合适的 View 响应事件传递是从自己查找 NextResponder
网友评论