iOS的框架分为:应用层、触摸层、媒体层、核心服务层、核心系统操作层以及内核和驱动层。

详见https://blog.csdn.net/ScheenDuan/article/details/134274203?spm=1001.2014.3001.5501

其中触摸事件涉及到触摸层中的UIKit中的UIResponse,只有继承了UIResponse的对象才能接受处理事件。

一、事件传递链(命中测试):

事件传递链用于确定哪个视图应该接收用户触摸或手势事件。当用户与界面发生交互时,系统会沿着视图层次结构从上到下传递触摸事件,并最终找到能够处理该事件的最小子视图。这个过程称为 Hit-Testing,即事件命中测试。

在 iOS 世界中,为了确定视图是否可以与用户交互,视图具有一个名为
hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? 的方法,一旦有来自用户的触摸事件,系统将调用此函数来告诉视图是否应该触发触摸事件或手势识别器。

这里先介绍这个相关方法:

  1. hitTest(_:with:):通过递归调用来确定哪个子视图最终应该接收触摸事件。
  2.  pointInside(_:with:):当系统需要确定某个触摸点是否在某个视图内时,会调用 pointInside(_:with:)。如果返回 true,表示点在视图内;如果返回 false,则表示点不在视图内。pointInside 只检查当前视图的边界,而不涉及子视图。

这两个方法是包含关系,见如下代码:

func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        // 检查触摸点是否在当前视图内
    if !point(inside: point, with: event) {
        return nil
    }
        
    if alpha < 0.01 || !isUserInteractionEnabled || isHidden {
        return nil
    }
       // 遍历子视图并递归查找最合适的响应视图
    for subview in subviews.reverse() {
         if let hitView = subview.hitTest(point, with: event) {
            return hitView
        }
    }
    return nil
}

具体的事件传递过程:

1. 事件的触发

• 当用户触摸屏幕时,系统会首先将触摸事件封装为 UIEvent 对象,传递给应用的窗口对象(UIWindow)。

2. UIWindow 开始传递

• UIWindow 会调用 hitTest(_:with:) 方法,通过事件位置查找出最适合接收触摸事件的视图。这通常是视图层次结构中的最小的子视图(也称为“命中的视图”)。

3. 视图层次结构中的传递

• hitTest(_:with:) 从根视图开始,递归检查每个子视图,使用 pointInside(_:with:) 方法判断触摸点是否在某个视图内。

• 找到合适的子视图后,它将作为事件的最终处理者。

4. 处理事件

• 如果视图对象能够处理事件,它会响应相关的事件处理方法,如 touchesBegan(_:with:)、touchesMoved(_:with:) 或 touchesEnded(_:with:)。

5. 未处理事件的传递

• 如果视图不能处理事件,或者选择不处理事件,它会将事件传递给其下一个响应者(next responder)。

• 这个传递过程会沿着响应链向上传递,最终可能传递到视图控制器(UIViewController)、父视图、窗口(UIWindow)、应用程序对象(UIApplication),甚至是系统对象。

总的来说,就是触摸了就生成一个事件,然后事件从父视图传递给子视图(寻找合适的响应视图),在这期间是用hitest方法去递归查找并且配合了point inside函数查看是否响应在了视图的边界范围内,当找到最适合的响应视图后,才会触发类似于touchesBegan之类的事件处理方法。

提一嘴,如果触摸点在视图外的话怎么进行处理?

  1. 通过重写 pointInside(_:with:) 方法,手动扩展视图的可交互区域。
  2. 在父视图中手动处理事件。
  3. 通过手势识别器或控制器全局捕获触摸事件。
  4. 在 UIViewController 级别处理触摸事件。
  5. 通过响应链将事件传递给下一个响应者。

二、响应链

响应链与事件的实际处理有关。当一个视图或控件接收到触摸事件之后,如果它不能处理该事件,它会将事件沿着 响应链 传递给其他对象(如其父视图或控制器)进行处理。响应链是基于 UIResponder 类实现的。

响应链的流程:

传递顺序:当一个对象无法处理事件时,事件会传递给它的下一个响应者(即 nextResponder)。如果下一个响应者也无法处理事件,则事件继续向上传递,直到到达根响应者(通常是 UIApplication 或 UIWindow)。

• nextResponder:每个 UIResponder 都有一个 nextResponder 属性,指向下一个响应者(通常是视图的父视图、视图控制器等)。

响应链的流程示例:

1. 用户点击按钮,但按钮没有处理事件。

2. 按钮会将事件传递给其父视图,父视图再传递给 UIViewController。

3. 如果 UIViewController 也不能处理,事件会传递给 UIWindow,最后传递给 UIApplication。

总的来说:响应链关注的是事件的“处理”过程,即当一个对象无法处理事件时,它将事件传递给谁。它提供了一个灵活的机制,使得即使某个控件无法处理事件,事件也可以通过 nextResponder 传递给其他对象进行处理。

传递链和响应链区别:

  • 事件传递链决定了谁接收事件,负责从顶层到子视图逐层传递事件。
  • 响应链则决定了谁处理事件,如果接收事件的视图无法处理,它可以将事件传递给下一个响应者。

参考:https://slk11075.medium.com/hittest-on-ios-ec3b869b57f9

不用但一定要懂 ---- iOS 之 响应链、传递链 与 手势识别_ios手势响应链-CSDN博客

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部