美文网首页
iOS 事件传递和响应

iOS 事件传递和响应

作者: 游城十代2dai | 来源:发表于2020-05-10 09:08 被阅读0次

    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

    相关文章

      网友评论

          本文标题:iOS 事件传递和响应

          本文链接:https://www.haomeiwen.com/subject/vfjdnhtx.html