「OC」iOS事件处理流程

「OC」初识iOS事件处理流程

文章目录

  • 「OC」初识iOS事件处理流程
    • 触摸事件
    • 触摸事件的响应周期
    • 事件 响应者
      • UIEvent
      • UITouch
      • UIResponder
    • 触摸流程
      • 系统响应阶段
      • APP响应阶段
        • 寻找最佳响应者
      • 构成响应链
    • 寻找最佳响应者和响应链的区别
    • 总结
    • 参考资料

触摸事件

iOS的事件有好几种:Touch Events(触摸事件)、Motion Events(运动事件,比如重力感应和摇一摇等)、Remote Events(远程事件,比如用耳机上得按键来控制手机),其中最常用的应该就是Touch Events了,基本存在于每个app的每个地方

触摸事件的响应周期

uitouchflow.png

事件 响应者

在学习之前,还需要将基本的概念了解清楚

UIEvent

UIEvent即为事件,事件一共被分为三类,包括触摸事件(Touch Events对应就是UITouch)、运动事件(Motion Events)、远程控制事件(Remote Control Events)。

触摸的目的是生成触摸事件供响应者响应,一个触摸事件对应一个UIEvent对象,其中的 type 属性标识了事件的类型(即三种不同的时间类型)。

当我们app获取到触摸事件的时候,就会将event放置到一个事件队列之中(先触发的事件先执行,符合队列先进先出的特点)

UITouch

一个手指一次触摸屏幕,就对应生成一个UITouch对象。多个手指同时触摸,生成多个UITouch对象。

多个手指先后触摸,系统会根据触摸的位置判断是否更新同一个UITouch对象。若两个手指一前一后触摸同一个位置(即双击),那么第一次触摸时生成一个UITouch对象,第二次触摸更新这个UITouch对象(UITouch对象的 tap count 属性值从1变成2);若两个手指一前一后触摸的位置不同,将会生成两个UITouch对象,两者之间没有联系。

每个UITouch对象记录了触摸的一些信息,包括触摸时间、位置、阶段、所处的视图、窗口等信息。

手指离开屏幕一段时间后,确定该UITouch对象不会再被更新将被释放。

在UIEvent之中使用以下方法可以获得UIEvent的touch信息:

NSSet *touches = [event allTouches];
for (UITouch *touch in touches) {// 访问每个 UITouch 对象的属性CGPoint location = [touch locationInView:view];NSTimeInterval timestamp = [touch timestamp];// 其他属性...
}

UIResponder

每个响应者都是一个UIResponder对象,即所有继承于自UIResponder的对象,本身都具备响应事件的能力。因此以下类的实例都是响应者:

  • UIView
  • UIViewController
  • UIApplication
  • AppDelegate

在有关触摸的内容,我们使用以下的方法

//手指触碰屏幕,触摸开始
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
//手指在屏幕上移动
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
//手指离开屏幕,触摸结束
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
//触摸结束前,某个系统事件中断了触摸,例如电话呼入
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;

触摸流程

系统响应阶段

在我们触摸屏幕的时候

  1. IOKit.framework封装整个触摸事件为IOHIDEvent对象
  2. OKit.framework通过IPC将事件转发给SpringBoard.app
  3. IOKit将触摸事件封装成一个IOHIDEvent对象,并通过mach port传递给SpringBord进程

mach port是各个进程的端口,各进程通过它来进行进程间通信
SpringBord是一个系统进程,可以理解为桌面系统。它用来统一管理系统接收到的触摸事件

APP响应阶段

前面的阶段,是通过硬件结合来进行的,接下来的阶段就是通过app来找到点击时,手指停留在view之中的位置,我们还需要将时事件传递给具体被点击的View上。 我们要处理这个问题,就是需要使用响应链。

img

寻找最佳响应者

在UIView之中存在以下两个方法

- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;  //返回触发点击的view
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;  //判断坐标在哪个view的管辖范围内

通过这两个方法,通过上面图片给出的流程,不断循环追溯到准确的响应子视图 ,第二个方法是包含在第一个方法之中的。

这一过程主要来确定由哪个视图来首先处理 UITouch 事件。当你点击一个 view,事件传到 UIWindow 这一步之后,会去遍历 view 层级,直至找到那个合适的 view 来处理这个事件,这一过程也叫做 Hit-Testing。而在传递至UIWindow之前,UIApplication先将事件通过 sendEvent: 传递给事件所属的window,window同样通过 sendEvent: 再将事件传递至view之中。

系统会根据添加 view 的前后顺序,确定 view 在 subviews 数组中的顺序。然后根据这个顺序将视图层级转化为图层树,针对这个树,使用倒着进行前序深度遍历的算法,进行遍历。

前序深度遍历的具体流程如下:

  • 如果点不在这个视图内,则去遍历其他视图。
  • 如果点击在这个视图内,但是其还有子视图,那么将事件传递给子视图,并且调用子视图的 [hitTest:withEvent:].
  • 如果点击在这个视图内,并且这个视图没有子视图,那么 return self,即它就是那个最合适的视图。
  • 如果点击在这个视图内,并且这个视图没有子视图,但是不想作为处理事件的 view,可以 return nil,事件由父视图处理。

注:以下三种情况UIView以及其子View的hitTest(_:with:)不会被调用,而且子UIView不接收任何触摸事件

userInteractionEnabled = NO
hidden = YES
alpha = 0.0~0.01之间(透明度<0.01即为透明)

UIImageViewuserInteractionEnabled默认为NO,因此UIImageView以及它的子控件默认是不接收触摸事件的。

通过以上内容,我们可以仿照一个相关的方法hitTest:withEvent:,以下直接照抄大佬给出的内容:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{//3种状态无法响应事件if (self.userInteractionEnabled == NO || self.hidden == YES ||  self.alpha <= 0.01) return nil; //触摸点若不在当前视图上则无法响应事件if ([self pointInside:point withEvent:event] == NO) return nil; //从后往前遍历子视图数组 int count = (int)self.subviews.count; for (int i = count - 1; i >= 0; i--) { // 获取子视图UIView *childView = self.subviews[i]; // 坐标系的转换,把触摸点在当前视图上坐标转换为在子视图上的坐标CGPoint childP = [self convertPoint:point toView:childView]; //询问子视图层级中的最佳响应视图UIView *fitView = [childView hitTest:childP withEvent:event]; if (fitView) {//如果子视图中有更合适的就返回return fitView; }} //没有在子视图中找到更合适的响应视图,那么自身就是最合适的return self;
}

构成响应链

最佳响应者的最佳,其实就是这个UIResponder对象具有响应对应事件的最高权限,每一个响应者对象(UIResponder对象)都有一个 nextResponder 方法,用于获取响应链中当前对象的下一个响应者。

  • 若视图是控制器的根视图,则其nextResponder为控制器对象;否则,其nextResponder为父视图。
  • UIViewController
    若控制器的视图是window的根视图,则其nextResponder为UIWindow对象;若控制器是从别的控制器present出来的,则其nextResponder为presenting view controller。
  • UIWindow
    nextResponder为UIApplication对象。
  • UIApplication
    若当前应用的app delegate是一个UIResponder对象,且不是UIView、UIViewController或app本身,则UIApplication的nextResponder为app delegate。

通过以上的方式我们就可以通过寻找nextResponder将响应链完整的构建出来。

响应者对于事件的拦截以及传递都是通过 touchesBegan:withEvent: 方法控制的,该方法的默认实现是将事件沿着默认的响应链往下传递。响应者链存在的意义为提供一种机制,让未被直接交互的对象也有机会处理事件,增加了事件处理的灵活性。

img

寻找最佳响应者和响应链的区别

  • 寻找最佳响应者:
    • 从最底层的视图开始,自下而上地检查视图层级。
    • 使用hitTest:withEvent:方法来确定哪个视图包含了触摸点。
    • 考虑视图的属性,如是否隐藏、是否启用用户交互等。
  • 响应者链:
    • 从最佳响应者开始,沿着预定义的路径向上传递。
    • 通常的路径是:UIView → UIViewController → UIWindow → UIApplication → UIApplicationDelegate。

总结

以上就是对触摸事件以及响应者链的学习内容,接下来还有UIResponder、UIGestureRecognizer、UIControl这个几个触发响应的优先级,以及响应事件内部的深入探究,由于篇幅我们便将剩下的内容留到下一篇博客吧。

参考资料

01 触摸事件传递

iOS事件处理

iOS——事件、响应链和传递链

iOS触摸事件全家桶

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/53153.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DriveLM的baseline复现

DriveLM是一篇很有意思的工作&#xff0c;把自动驾驶跟MLLM结合到一起了&#xff0c;实现端到端的感知or决策规划。 Repo&#xff1a;https://github.com/OpenDriveLab/DriveLM 该工作是基于nuScenes数据集做的&#xff0c;官方paper里给出了数据的具体构建方式&#xff0c;感…

SpringBoot依赖之Spring Boot DevTools热部署开发增效工具

摘要&#xff1a;Spring项目又大又重&#xff0c;依赖多&#xff0c;编译启动慢&#xff0c;怎么提高研发效率呢&#xff1f;方法之一热部署&#xff01; 概念 Spring Boot DevTools 依赖名称: Spring Boot DevTools功能描述: Provides fast application restarts, LiveRelo…

25k的自动化测试面试题,原来都是这样~

小编热衷于收集整理资源&#xff0c;记录踩坑到爬坑的过程。希望能把自己所学&#xff0c;实际工作中使用的技术、学习方法、心得及踩过的一些坑&#xff0c;记录下来。也希望想做软件测试的你一样&#xff0c;通过我的分享可以少走一些弯路&#xff0c;可以形成一套自己的方法…

AI绘画时代的自媒体引流攻略:如何实现粉丝暴涨与盈利

一、AI绘画在自媒体引流和赚钱中的应用 创作独特视觉内容&#xff0c;吸引粉丝关注 AI绘画技术可以帮助自媒体从业者创作出独一无二的视觉内容&#xff0c;这些内容在社交媒体上具有很高的辨识度和吸引力。通过以下方式&#xff0c;AI绘画助力引流和赚钱&#xff1a; &#xf…

SprinBoot+Vue图书馆预约与占座微信小程序的设计与实现

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 application.yml3.5 SpringbootApplication3.5 Vue3.6 uniapp代码 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍&#xff1a;CSDN认证博客专家&#xff0c;CSDN平…

SpringBoot+Vue实现大文件上传(断点续传-后端控制(一))

SpringBootVue实现大文件上传&#xff08;断点续传&#xff09; 1 环境 SpringBoot 3.2.1&#xff0c;Vue 2&#xff0c;ElementUI&#xff0c;spark-md5 2 问题 在前一篇文章&#xff0c;我们写了通过在前端控制的断点续传&#xff0c;但是有两个问题&#xff0c;第一个问题&…

铁打的程序员轻易“不哭”-我的大模型创业近2年来的感悟

楔子 2022年11月&#xff0c;GPT-3发布那一刻&#xff0c;我被AI的强大能力所震撼&#xff0c;意识到“超级个体”时代的来临。自那时起&#xff0c;我开始全心投入创业&#xff0c;经历了许多苦乐交织的时光。 2023年6月&#xff0c;我尝试将AI应用于智能营销导购&#xff0…

云原生架构概念

云原生架构概念 云原生架构&#xff08;Cloud Native Architechtrue&#xff09;作为一种现代软件开发的革新力量&#xff0c;正在逐渐改变企业构建、部署和管理应用程序的方式。它的核心优势在于支持微服务架构&#xff0c;使得应用程序能够分解为独立、松耦合的服务&#xf…

window系统怎么设置闹钟提醒?分享一个桌面提醒设置办法

在日常工作和生活中&#xff0c;我们常常会因忙碌而遗忘一些重要事项。对于很多使用电脑办公的用户来说&#xff0c;如果能在桌面上设置闹钟提醒&#xff0c;无疑会大大提高工作效率&#xff0c;减少遗漏。那么&#xff0c;如何设置这样的闹钟提醒呢&#xff1f; 这时&#xf…

ElementUI实现el-table组件的合并行功能

前言 有时遇到一些需求&#xff0c;需要实现ElementUI中&#xff0c;el-tabled组件合并单元格的功能&#xff0c;稍微了解一下它的数据格式&#xff0c;不难可以写出比合并方法。但是在鼠标经过单元行时&#xff0c;会出现高亮的行与鼠标经过的行不一致的BUG。因此还需要实现c…

8月刷题笔记

刷题笔记—8月 LCP40.心算挑战(贪心、排序) class Solution { public:int maxmiumScore(vector<int>& cards, int cnt) {//24.8.1ranges::sort(cards, greater()); //从大到小排序int s reduce(cards.begin(), cards.begin()cnt, 0);if(s%2 0) return s;auto rep…

无线麦克风哪个牌子的好,麦克风哪个好,无线麦克风品牌推荐

​在自媒体日益繁荣的当下&#xff0c;内容创作成为了许多人追求的目标。对于视频内容创作者而言&#xff0c;出色的内容是成功的基石&#xff0c;而高质量的设备则是保证作品品质的关键。为了提升视频音质&#xff0c;拥有一款专业的无线麦克风是不可或缺的。 然而&#xff0…

PHP智能匹配轻松预订自习室在线订座系统小程序源码

智能匹配&#xff0c;轻松预订——自习室在线订座系统 &#x1f4da;【开篇&#xff1a;告别排队&#xff0c;迎接智能学习新时代】&#x1f4da; 还在为找不到合适的自习室座位而烦恼吗&#xff1f;是不是每次去图书馆或自习室都要提前好久去排队占位&#xff1f;现在&#…

太速科技-1路万兆光纤SFP+和1路千兆网络 FMC子卡模块

1路万兆光纤SFP和1路千兆网络 FMC子卡模块 一、概述 该板卡是基于kc705和ml605的fmc 10g万兆光纤扩展板设计&#xff0c;提供了1路万兆光纤SFP和1路千兆网络接口。可搭配我公司开发的FPGA载卡使用。载卡可参考&#xff1a;ID204 SFP&#xff08;10 Gigabit Small…

AWS-亚马逊网络服务(基础服务)-AWS 定价计算器-概述与动手部署:

让我们来概述并亲身实践如何使用 AWS 定价计算器来计算 概述&#xff1a; AWS 定价计算器是 Amazon Web Services (AWS) 提供的基于 Web 的工具&#xff0c;可帮助用户估算其特定用例的 AWS 服务成本。欢迎来到雲闪世界。 它允许客户建模他们的基础设施并根据他们打算使用的…

Android 9.0 增加interface audio接口,解决编译报错

最近修改Android接口&#xff0c;报了一个VNDK的错误 我总结了如下几种方式&#xff1a; 1、直接关闭&#xff08;不推荐&#xff09;&#xff1a; 在BoardConfig.mk中加入如下两行&#xff0c;可以在编译的时候不去check VNDK&#xff0c;关掉这个可能会导致XTS某些测项跑不…

Burp Suite Professional 2024.8 for macOS x64 ARM64 - 领先的 Web 渗透测试软件

Burp Suite Professional 2024.8 for macOS x64 & ARM64 - 领先的 Web 渗透测试软件 世界排名第一的 Web 渗透测试工具包 请访问原文链接&#xff1a;https://sysin.org/blog/burp-suite-pro-mac/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页…

Linux内核编程(十五)网络设备驱动

本文目录 一、常见的网络协议二、传输介质三、RJ-45接口 对于网络知识不太熟悉的同学可以查看这篇文章&#xff1a;计算机网络知识点详情总结。 一、常见的网络协议 TCP、UDP协议&#xff1a;详情查看-TCP、UDP系统编程。DNS协议&#xff1a;是互联网中用于将域名&#xff08…

RocketMQ集群搭建,及RocketMQ-Dashboard部署(前RocketMQ-Console)

集群搭建 RocketMQ不支持单主机搭建主从结构集群&#xff0c;当从节点启动时&#xff0c;即使和主节点设置不同的监听端口&#xff0c;他也要去监听主节点端口&#xff0c;也就是说正常启动的从节点会监听四个端口。原因未知&#xff0c;现象后面会列举出来。 1. 准备JAVA环境…

在 Ubuntu 环境下使用 VSCode 和 PlatformIO 下载程序到 Arduino Uno

安装 VSCode 访问 VSCode 官网 下载 .deb 包使用以下命令安装&#xff1a;sudo dpkg -i <下载的文件名>.deb sudo apt-get install -f安装 PlatformIO 扩展 在 VSCode 中&#xff0c;转到扩展市场&#xff08;CtrlShiftX&#xff09;搜索 “PlatformIO IDE”点击 “安装”…