温故而知新-iOS事件响应链

温故而知新-iOS事件响应链

IOS小彩虹2021-08-17 0:43:27280A+A-

简介

用一句话概括响应链过程:先寻找第一响应者,若它无法处理该事件,则传递给下一响应者。

大致的响应过程如图(左边为一个手机界面,右边对应此界面的响应过程)

20210315-104757.png

  1. 当一个触摸事件生成时,系统会将其加入到 UIApplication 管理的事件队列中。
  2. UIApplication 会取出队列最前面事件,分发给合适的 Window。
  3. Window 会在当前视图中,寻找最合适的视图来响应事件,即第一响应者。
  4. 视图主要是借助 hitTest(_:with:) 方法,来寻找最合适的子视图中以响应事件。
  5. 当找到最合适视图后,若该视图无法处理此事件,则传递给下一响应者。

寻找第一响应者过程

可以这样概括:

向每个 subView 询问在不在你的范围内,在的话,就继续往里找,直到没有 subView 时,返回自己。

核心就是 hitTest(_:with:) 方法,大致逻辑如下:

extension UIView {
    func myHitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        guard self.isHidden == false, self.alpha > 0, // 没隐藏
                self.isUserInteractionEnabled == true, // 可交互
                self.point(inside: point, with: event) == true /* 在范围内 */ else {
            return nil
        }

        for v in self.subviews.reversed() { // 从最上层的 view 开始寻找最佳响应者
            let p = self.convert(point, to: v)
            if let firstResponder = v.myHitTest(p, with: event) {
                return firstResponder
            }
        }

        // 无 subView 或 找不到能响应的 subView,则返回自己
        return self
    }
}

事件传递给下一响应者

当找到最合适视图后,若该视图无法处理该事件,则传递给下一响应者。

比如,label.isUserInteractionEnabled = true,但是没有添加手势,此时即使点击 label,它也无法响应,会将事件传给 nextResponder。

应用

如何响应 view 之外的事件?

覆写 pointInside:withEvent: 方法,在寻找事件接收者时,扩大其响应区域。

实际开发中,可能会借助关联对象和 Category 配合实现。

那可不可以通过重写 hitTest 方法来实现呢? 非要这样做也可以,只是要将 -[UIView pointInside:withEvent:] 的逻辑在 hitTest 中完成。 但这样破坏了原有链路,而且远没有直接在 pointInside 里处理方便。

其他

关于寻找第一响应者的解释

官方文档

Determining Which Responder Contained a Touch Event UIKit uses view-based hit-testing to determine where touch events occur. Specifically, UIKit compares the touch location to the bounds of view objects in the view hierarchy. The hitTest(_:with:) method of UIView traverses the view hierarchy, looking for the deepest subview that contains the specified touch, which becomes the first responder for the touch event.

hitTest 会被调用多次

笔者测试时,发现即使是单次点击,hitTest 也会被调用多次。 google 后发现这是正常的:Apple - Lists.apple.com

感谢

iOS 中事件的响应链和传递链 - 掘金 Using Responders and the Responder Chain to Handle Events | Apple Developer Documentation 参考 最好的输入方式:iOS 中的触摸事件 - 简书

点击这里复制本文地址 以上内容由权冠洲的博客整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

支持Ctrl+Enter提交

联系我们| 本站介绍| 留言建议 | 交换友链 | 域名展示
本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除

权冠洲的博客 © All Rights Reserved.  Copyright quanguanzhou.top All Rights Reserved
苏公网安备 32030302000848号   苏ICP备20033101号-1
本网站由 提供CDN/云存储服务

联系我们