iOS_响应者链 Responder Chain

文章目录

简述

  • 传递链:

    系统向离用户最近的view传递。

    UIKit --> active app's event queue --> window --> root view --> ... --> lowest view

  • 响应链:

    由离用户最近的view向系统传递。

    initial view --> super view --> ... --> view controller --> window --> Application --> AppDelegate

iOS中,只有继成UIResponder的对象才能接收并处理事件,UIReaponder是所有响应对象的基类,在UIResponder类中定义了处理上述各种事件的接口。UIApplication、UIViewController、UIWindow和所有继承直UIView和UIKit类都直接或间接的继承UIResponder,所以它们的实例都是可以构成响应者链的响应者对象。


Hit-Test 机制 (找到最佳响应者)

用户触摸,Touch -> UIEvent -> eventQueue -> UIApp -> UIWindow -> hitTest:withEvent: 查找触摸点所在视图。

当用户触摸屏幕时,系统首先要找到响应者Responder。将Touch以UIEvent的方式加入到UIApplication的事件队列中。UIApplication从事件队列中取出最新的事件传递到UIWindow。UIWindow通过 hitTest:withEvent: 方法寻找触摸点所在的视图,这个过程称之为hit-test view。

UIApplication -> UIWindow -> RootView -> supView -> View

在顶级视图(Root View)上调用pointInside:withEvent:方法判断触摸点是否在当前视图内;

如果返回NO,那么hitTest:withEvent:返回nil;

如果返回YES,那么它会向当前视图的所有子视图发送hitTest:withEvent:消息,所有子视图的遍历顺序是从最顶层视图一直到到最底层视图,即从subviews数组的末尾向前遍历,直到有子视图返回非空对象或者全部子视图遍历完毕。

响应者链 Responder chain

当系统通过Hit-Test机制找到触摸到的View,但是view没有或无法正确处理此次Touch。此时,系统便会通过响应者链寻找下一个响应者,对此次Touch进行相应:

View -> View Controller(如果存在)-> superView -> RootView -> UIWindow -> UIApplication


hitTest 实现原理大致如下:

objectivec 复制代码
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
  // 1、控件不允许与用户交互
  if (self.userInteractionEnabled == NO ||
      self.alpha <= 0.01 ||
      self.hidden == YES) {
    return nil;
  }
  // 2、点击的point不在当前控件内
  if (![self pointInside:point withEvent:event]) {
    return nil;
  }
  // 3、倒序遍历每一个子控件
  for (int i = (int)self.subviews.count - 1; i >= 0; i--) {
    UIView *childView = self.subviews[i];
    // 当前触控Point的坐标转换为相对于子控件的坐标
    CGPoint childPoint = [self convertPoint:point toView:childView];
    // 在子控件中找能响应的子控件(递归循环),从上层找起
    UIView *fitView = [childView hitTest:childPoint withEvent:event];
    if (fitView) {
      return fitView;
    }
  }
  // 4、子视图中没有能响应的view,就返回自己
  return self;
}

// 该方法判断触摸点是否在控件上,point必须是方法调用者的坐标
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    return NO;
}

应用

获取当前View的控制器对象:

objectivec 复制代码
- (UIViewController *)getCurrentViewController{ 
    UIResponder *next = [self nextResponder]; 
    do { 
        if ([next isKindOfClass:[UIViewController class]]) { 
            return (UIViewController *)next; 
        } 
        next = [next nextResponder]; 
    } while (next != nil); 
    return nil; 
}

手势穿透:

objectivec 复制代码
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    UIView *view = [super hitTest:point withEvent:event];
    if (self == view) {
        return nil;
    }
    return v;
}
相关推荐
Deng桑6 小时前
Linux线上排查命令
1024程序员节
comyccccc7 小时前
STM32F103C8T6最小系统板设计全解析学习笔记
单片机·1024程序员节
叼烟扛炮10 小时前
Linux 学习笔记(三十三):线程互斥——互斥锁(Mutex)与死锁
linux·运维·服务器·笔记·学习·1024程序员节
云和数据.ChenGuang11 小时前
Python 常见特殊方法详解:__new__、__init__、__del__、__str__、__repr__ 与 __getattribute__
开发语言·人工智能·python·1024程序员节
海宇服务11 小时前
零信任架构实战:基于海宇全国车辆配置查验构建自动化二手商用车出厂参数建档网关
运维·人工智能·架构·自动化·1024程序员节
zhippt_101412 小时前
AI生成PPT工具推荐:图转PPT与一键生成实测对比
人工智能·1024程序员节
鬼手点金13 小时前
KV Cache 未必是一次性的:可编辑、可组合的“笔记”
java·人工智能·笔记·学习·自动化·1024程序员节
深圳光特通信豆子13 小时前
使用光特通信光模块构建核心-分布-接入三层园区网络
1024程序员节
喜欢猪猪14 小时前
把记忆搬出显存:DeepSeek-V4.1-Flash Engram 与“第二稀疏轴“的架构革命
1024程序员节
4SAPI15 小时前
多模态 AI 应用统一接入网关生产选型指南:4SAPI、硅基流动与 OpenRouter 的架构权衡
大数据·人工智能·架构·php·1024程序员节