当前位置: 首页 > news >正文

iOS ------ 事件响应链

响应者链

响应者链是由一系列链接在一起的响应者(UIResponser之类:UIApplicationUIViewControllerUIView)注组成的。一般情况下,一条响应链开始于第一响应者,结束于application对象。如果一个响应者不能处理事件,会将事件沿着响应者链传到下一个响应者

响应者对象

在响应者链中,每个响应者对象都可以处理事件,也可以选择将事件传递给下一个响应者对象进行处理,或者直接丢弃事件。响应者链中的每个响应者对象都可以重写几个方法来处理事件,这些方法包括touchesBegan:withEvent:touchesMoved:withEvent:touchesEnded:withEvent:等等。

响应者对象都实现了UIResponder协议,这里的实现UIResponder协议指UIApplication、UIViewController、UIView 都继承于 UIResponder。

常见的响应者对象

  • UIView:是iOS中最基本的用户界面元素,可以接收用户的触摸事件并进行相关的处理。
  • UIViewController:作为MVC模式中的控制器,可以响应用户的触摸事件,同时还可以管理一个或多个视图控制器。
  • UIWindow:是整个应用程序的窗口,它包含了一个或多个视图,并且是接收和处理触摸事件的最高层响应者对象。
  • UIGestureRecognizer:是iOS中专门用来处理手势事件的响应者对象,包括UITapGestureRecognizer、UIPanGestureRecognizer、UILongPressGestureRecognizer等等。
  • UIScrollView:是一个可以滚动的视图控件,它可以接收用户的触摸事件,并在触摸拖动时进行滚动。
  • UITableView:是iOS中常用的列表视图控件,它可以显示大量的数据,并且可以处理用户的滑动、点击等事件

响应者事件

iOS 中的事件可分为:触摸事件(multitouch events)、加速计事件(accelerometer events):包括摇晃、倾斜、加速等设备运动、远程控制事件(remote control events):可以来自于耳机、锁屏界面和控制中心等,例如暂停音乐、切换歌曲等。

基本元素的了解

系统是怎么响应用户的触屏事件,这里有与用户事件相关的类,它们分别是UITouchUIEventUIResponder

UIResponder(事件响应者)

UIResponder是iOS中的一个基类,定义了一些接口,用于处理触摸事件和键盘事件。所有能够接受并处理事件的对象都继承于UIResponder

  • UIResponder内部提供了以下方法来处理事件
// 一根或者多根手指开始触摸view,系统会自动调用view的下面方法
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
// 一根或者多根手指在view上移动,系统会自动调用view的下面方法(随着手指的移动,会持续调用该方法)
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
// 一根或者多根手指离开view,系统会自动调用view的下面方法
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
// 触摸结束前,某个系统事件(例如电话呼入)会打断触摸过程,系统会自动调用view的下面方法[可选]
- (void)touchesCancelled:(nullable NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;

通过重写UIresponder中定义的方法,开发者可以自己类中处理用户事件,并做出相应的事件

UITouch(触摸)

  • UITouch是触摸对象,一个手指一次触摸屏幕就会生成一个UITouch对象
  • 若两个手指先后触摸同一个位置,第一次触摸时生成一个UITouch对象,第二次触摸更新UITouch对象的tapCount属性值由1变成2;如果两个手指一前一后触摸的位置不同,将会生成两个UITouch对象,两者没有联系。
  • 每个UITouch对象会记录触摸的一些记录,包括触摸时间、位置、阶段、所处的视图和窗口等信息。 当手指移动时,系统会更新同一个UITouch对象,使之能够保存该手指的触摸位置;当手指离开屏幕上时,系统会销毁响应的UITouch对象
  • UITouch对象会在触摸事件的过程中不断更新,直到触摸事件结束。在触摸事件的过程中,系统会不断向响应链的响应者发生事件,并将触摸事件封装成UIEvent对象进行传递。当触摸事件结束时,系统就会销毁相应的UITouch对象

UITouch的属性

// 记录了触摸事件产生或变化时的时间,单位是秒 The relative time at which the acceleration event occurred(read-only)
@property(nonatomic,readonly) NSTimeInterval      timestamp;
// 当前触摸事件所处的状态
@property(nonatomic,readonly) UITouchPhase        phase;
// touch down within a certain point within a certain amount of timen 短时间内点按屏幕的次数,可以根据tapCount判断单击、双击或更多的点击
@property(nonatomic,readonly) NSUInteger          tapCount;   
@property(nonatomic,readonly) UITouchType         type NS_AVAILABLE_IOS(9_0);
// 触摸产生时所处的窗口
@property(nullable,nonatomic,readonly, strong) UIWindow *window;
// 触摸产生时所处的视图
@property(nullable,nonatomic,readonly, strong) UIView   *view;
// The gesture-recognizer objects currently attached to the view.
@property(nullable,nonatomic,readonly,copy)   NSArray <UIGestureRecognizer *> *gestureRecognizers

UITouch方法

/*返回值表示触摸在view上的位置
这里返回的位置是针对view的坐标系的(以view的左上角为原点(0, 0))
调用时传入的view参数为nil的话,返回的是触摸点在UIWindow的位置*/
- (CGPoint)locationInView:(nullable UIView *)view;
// 该方法记录了前一个触摸点的位置
- (CGPoint)previousLocationInView:(nullable UIView *)view;// Use these methods to gain additional precision that may be available from touches.
// Do not use precise locations for hit testing. A touch may hit test inside a view, yet have a precise location that lies just outside.//获取指定视图上的精确触摸位置,该方法会考虑到多点触控时不同触点之间的偏移。
- (CGPoint)preciseLocationInView:(nullable UIView *)view API_AVAILABLE(ios(9.1));
// 获取指定视图上上一次触摸的精确位置。
- (CGPoint)precisePreviousLocationInView:(nullable UIView *)view API_AVAILABLE(ios(9.1));

UIEvent(事件)

UIEvent 是 iOS 中用于表示触摸事件的类,一个 UIEvent 对象包含了所有与触摸事件相关的信息,比如触摸的位置、时间、阶段,以及多点触控时不同触点之间的状态等等。UIEvent 对象是由系统自动创建和管理的,通常情况下不需要手动创建。每产生一个事件,就会产生一个 UIEvent 对象,UIEvent 称为事件对象。

事件类型属性

//事件类型,枚举值包括触摸、运动、遥控等。
@property(nonatomic,readonly) UIEventType     type NS_AVAILABLE_IOS(3_0);// 事件子类型,对于触摸事件,其子类型包括touch down、touch move、touch up等。
@property(nonatomic,readonly) UIEventSubtype  subtype NS_AVAILABLE_IOS(3_0);
产生时间的事件属性
事件发生的时间戳,单位为秒。
@property(nonatomic,readonly) NSTimeInterval  timestamp;

事件的传递和响应

在这里插入图片描述

  • 步骤一:寻找目标,在iOS的视图层次结构中找到事件的最终接受者
  • 步骤二:事件响应·,基于iOS响应者链处理触摸事件

事件的传递:寻找事件的第一响应者(Hit_Testing)

当一个事件发生时,事件会从父控件传给子控件
也就是说由

  • 硬件 -> 系统 -> UIApplication -> UIWindow -> SuperView -> SubView

以上就是事件的传递,也就是寻找第一响应者的过程。

符合第一响应者的条件包括:

  • touch事件的位置在响应者区域内 pointInside:withEvent: == YES
  • 响应者 self.hidden != NO
  • 响应者 self.alpha > 0.01
  • 响应者 self.userInteractionEnabled = YES
  • 遍历 subview 时,是从上往下顺序遍历的,即 view.subviews 的 + + + lastObject 到 firstObject 的顺序,找到合适的响应者view,即停止遍历.

第一响应者对于接收到的事件的三种操作:

  • 不拦截,默认操作。事件会自动沿着默认的响应者链往下传递
  • 拦截,不再往下分发事件。重写 touchesBegan:withEvent: 进行事件处理,不调用父类的 touchesBegan:withEvent:
  • 拦截,继续往下分发事件。重写 touchesBegan:withEvent: 进行事件处理,同时调用父类的 touchesBegan:withEvent: 将事件往下传递

事件的响应:一旦事件的第一响应者确定了,这个事件的响应链就确定了

下图是官网对于响应者链的实例展示

在这里插入图片描述

每个响应者对象(UIResponder)对象都有一个nextResponder方法,用于获取响应者链中当前对象的下一个响应者。

  • 图中虚线箭头是指若该UIView是作为UIViewController根视图存在的,则其nextResponderUIViewController对象;
  • 若是直接add在UIWindow上的,则其nextResponderUIWindow对象。

若触摸发生在UITextField上,则事件的传递顺序是:

  • UITextField ——> UIView ——> UIView ——> UIViewController ——> UIWindow ——> UIApplication ——> UIApplicationDelegation

虽然两个传递过程都设计到父子控件的传递,但它们的传递顺序和目的不同,触摸事件的传递过程主要是为了找到最合适的空间来处理事件,而响应者链传递过程是为了让控件的响应者对象能够逐级处理事件。

事件的生命周期

手指触摸屏幕的一刻,系统会生成一个触摸事件。经过IPC进程间通信,事件最终被传递给了合适的应用。

(一)系统响应阶段

  • 屏幕感应到触碰后,将事件交给IOKit处理,IOKit是监测硬件的框架。IOKit将触摸事件封装成一个IOHIDEvent对象,并通过mach port传递给SpringBoard进程。

mach port是进程端口,各个进程之间通过它进行通信;
SpringBoard.app是一个系统进程,可以理解为桌面系统,可以统一管理和分发系统接收到的触摸事件;

  • SpringBoard.app进程收到触摸事件,触发主线程RunLoop的source1事件源的回调。SpringBoard.app会根据当前桌面的状态,判断应该由谁响应此次触摸事件。如果没有APP在运行,则由SpringBoard处理该事件;如果有APP在运行,则由APP处理该事件;

(二)APP响应阶段

  • APP进程的mach port接收到SpringBoard进程传递来的触摸事件,主线程的RunLoop被唤醒,触发source1回调;
  • source1回调触发了一个source0回调,将接收到的IOHIDEvent对象封装成UIEvent对象;
  • source0回调内部将触摸事件添加到UIApplication对象的事件队列中。事件出队列后,UIApplication开始寻找一个最佳响应者的过程,这个过程又称为hit-testing,具体细节在第二个主题寻找最佳响应者中阐述;
  • 找到最佳响应者后,事件就在响应链中传递和响应,这里涉及到“事件的响应和响应链中的传递”;
  • 经过上述流程,触摸事件要么被某个响应对象捕获后释放,要么没有找到能响应的对象被释放;

总结:触摸事件从触屏产生后,有IOKit将触摸事件传递给SpringBoard进程,再由SpingBoard分发给当前前台APP处理,触发事件响应者链事件。

完整的触摸过程

一个完整的触摸事件流程通常包括以下几个步骤:

  • 手指触摸到屏幕,系统会创建一个与手指相关联的 UITouch 对象,并将其加入到系统中的事件队列中。
  • 系统会将该事件发送给当前 UIWindow 对象,即调用 UIWindow 对象的 touchesBegan(_:with:) 方法,并将该事件传递给子视图。
  • 从根视图开始,系统会通过递归调用 hitTest(_:with:) 方法,寻找响应该事件的视图。在每个视图中,系统都会调用 point(inside:with:) 方法,判断该视图是否包含该事件的触摸点。
  • 一旦找到了响应该事件的视图,系统会将该事件发送给该视图,即调用该视图的 touchesBegan(:with:) 方法。
  • 在该视图的 touchesBegan(:with:) 方法中,开发者可以对该事件做出相应的处理,比如更改视图的状态、更新视图的内容等。
  • 如果该事件需要传递给其它视图进行处理,开发者可以手动调用 next 方法,将该事件传递给下一个响应者。
  • 当手指离开屏幕时,系统会将一个 touch 对象的 phase 属性设置为 .ended,并将该 touch 对象从事件队列中移除。
  • 当前的 UIWindow 对象会将该事件发送给响应者链中的下一个响应者。如果没有下一个响应者,则该事件的响应过程结束。

总结:

  • 当触摸事件发生后,系统会自动生成一个UIEvent对象,记录事件发生的事件和类型
  • 然后系统会把UIEvent事件加入到一个由UIApplocation管理的事件队列中
  • 然后UIApplication会讲事件分发给UIWindow,主窗口会在视图层次结构中找到一个合适的响应者对象来处理触摸事件。
  • 不断递归调用hitTest方法来找到第一响应者
  • 如果第一响应者无法响应事件,那么会按照响应者链往上传递,也就是传递给自己的父视图
  • 一直传递直到UIApplication,如果都无法响应,事件就会被丢弃

相关文章:

iOS ------ 事件响应链

响应者链 响应者链是由一系列链接在一起的响应者&#xff08;UIResponser之类&#xff1a;UIApplication&#xff0c;UIViewController&#xff0c;UIView&#xff09;注组成的。一般情况下&#xff0c;一条响应链开始于第一响应者&#xff0c;结束于application对象。如果一个…...

Go 语言 switch 语句的特点

在 Go 语言中&#xff0c;switch 语句设计得更加简洁和直观&#xff0c;因此不需要显式使用 break 语句来终止一个分支。这种设计决策源于 Go 语言的一些设计哲学和目标&#xff0c;主要包括&#xff1a; 自动终止&#xff1a; Go 语言的 switch 语句会在每个 case 执行完成后自…...

【递归】什么是递归-C语言为例

递归是指一个函数在其定义中直接或间接调用自身的编程技巧。在C语言中&#xff0c;递归常用于解决可以被分解为更小的子问题的问题。递归函数通常由两个主要部分组成&#xff1a; 基准情况&#xff1a;这是递归停止的条件&#xff0c;通常是最简单的情况。 递归情况&#xff1…...

vue针对低版本浏览器不兼容es6特性解决方案,

browser.min.js 解决ES6兼容IE browser.min.js&#xff0c;polyfill.min.js vue针对安卓低版本、ios9 不兼容 es6特性解决方案 解决IE9无法使用promise的js脚本&#xff0c;引入后&#xff0c;还需跟browser.js配合使用 Babel 默认只转换新的 JavaScript 句法&#xff0c;po…...

嵌入式内存管理高频面试题及参考答案(4万字长文)

目录 嵌入式系统中内存管理的重要性 嵌入式系统中的内存主要分为哪几类? 静态内存分配和动态内存分配的特点 内存对齐的概念及其作用 嵌入式系统中为什么需要关注内存碎片问题 内存分区的概念及其在嵌入式系统中的应用 内存映射文件的概念及其在嵌入式系统中的作用 虚…...

TinyWebserver的复现与改进(2):项目的整体框架

上文我们成功运行了代码&#xff0c;本文我们将对项目的整体流程作一下讲解 如果你之前没做过相关的内容&#xff0c;对服务器的⾼并发模型也⼀⽆所知&#xff0c;不建议继续做下去&#xff0c;需要的前置知识有&#xff1a; Linux的基本命令&#xff08;⭐&#xff09;多进程…...

R 语言学习教程,从入门到精通,R 字符串(10)

1、R 字符串 R 语言字符串可以使用一对单引号 ’ ’ 或一对双引号 " " 来表示。 单引号字符串中可以包含双引号。 单引号字符串中不可以包含单引号。 双引号字符串中可以包含单引号。 双引号字符串中不可以包含双引号。 以下示例演示来字符串的使用&#xff1a; a …...

QT 简易音乐播放器

目录 放置控件 获取mp3文件 播放音乐 准备工作 加载模块 加载头文件 new一个output对象,Mediaplayer对象 把outpout对象交给mediaplayer对象 给播放器设置音乐 播放 优化 上一曲下一曲功能 双击歌曲播放 获取音乐时长和音乐播放时间 让音乐进度条跟随音乐走 调…...

代码随想录八股训练营day32

代码随想录八股训练营day32 1、synchronized和lock的区别是什么 &#xff08;1&#xff09;synchronized和lock的区别是什么 synchronized和Lock都是Java中用于实现线程同步的手段&#xff0c;synchronized是Java的关键字&#xff0c;基于JVM的内置锁实现&#xff0c;可以用于…...

11.面试题——消息队列RabbitMQ

1.RabbitMQ是什么&#xff1f;特点是什么&#xff1f; RabbitMQ是一种开源的消息队列中间件&#xff0c;用于在应用程序之间进行可靠的消息传递。它实现了AMQP&#xff08;Advanced Message Queuing Protocol&#xff09;协议&#xff0c;提供了强大的消息处理能力。RabbitMQ的…...

MySQL运维-日志

错误日志 二进制日志 介绍 日志格式 日志查看 日志删除 查询日志 慢查询日志...

synchronized重量级锁的实现原理是怎样的

重量级锁&#xff08;Heavyweight Locking&#xff09;是 Java 中 synchronized 关键字的锁机制的一部分&#xff0c;用于在高竞争情况下确保线程的同步。重量级锁主要通过操作系统的线程同步机制实现&#xff0c;通常涉及阻塞线程、上下文切换等开销较大的操作。以下是重量级锁…...

探索 GLTF 的世界:3D 内容的未来

在 3D 内容创作领域&#xff0c;GLTF 正在掀起波澜&#xff0c;成为跨不同平台提供丰富互动体验的未来标准。GL 传输格式 &#xff08;GLTF&#xff09; 由 Khronos Group 开发&#xff0c;是一种用于在工具和服务之间传输 3D 模型和场景的开放标准。它设计紧凑、高效且易于集成…...

【深度学习】【语音】TTS, CM-TTS,TTS扩散模型,论文

CM-TTS: Enhancing Real Time Text-to-Speech Synthesis Efficiencythrough Weighted Samplers and Consistency Models CM-TTS: 提高实时文本到语音合成效率 通过加权采样器和一致性模型 Xiang Li 1, Fan Bu 1, Ambuj Mehrish 2, Yingting Li 1, Jiale Han 1, Bo Cheng 1, S…...

【网络协议】网络劫持 - ARP_DNS欺骗篇

前言 网络劫持是一种网络攻击技术&#xff0c;攻击者通过拦截、篡改或重定向数据流量&#xff0c;控制用户的网络通信路径&#xff0c;干扰正常的网络服务。其方式可能包括DNS劫持、ARP欺骗和HTTP劫持等。通过这些手段&#xff0c;攻击者可以窃取敏感信息如个人身份数据和财务信…...

Linux 系统框架分析(一)

一、linux内核结构框图 对内核结构框图有个总体的把握&#xff0c;有助于理解为什么驱动要这样写&#xff0c;为什么写的应用程序所用的C库接口能够产生这么多的事情。 框图可以看出来&#xff0c;linux系统&#xff0c;包括五个系统 一、Linux内核结构介绍 Linux 内核是操作…...

Leetcode 剑指 Offer II 090.打家劫舍 II

题目难度: 中等 原题链接 今天继续更新 Leetcode 的剑指 Offer&#xff08;专项突击版&#xff09;系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~ 题目描述 一个专业的小偷&#xff0c;计划偷窃一个环形街道上沿街的房屋&a…...

上海冷链配送新篇章 华鼎冷链科技以卓越服务餐饮品牌

在快速发展的上海餐饮连锁行业中&#xff0c;冷链运输作为保障食品安全与品质的关键环节&#xff0c;正迎来前所未有的发展机遇与挑战。华鼎冷链科技作为该领域的佼佼者&#xff0c;正引领着上海乃至全国冷链运输行业的新风尚。 华鼎冷链科技的成功并非一蹴而就。首先&#xff…...

学习鸿蒙-应用市场申请签名

1.需要的文件概念 .cer / .p7b / .p12 / .csr HarmonyOS应用/服务通过数字证书&#xff08;.cer文件&#xff09;和Profile文件&#xff08;.p7b文件&#xff09;来保证应用/服务的完整性。在申请数字证书和Profile文件前&#xff0c;首先需要通过DevEco Studio来生成密钥&am…...

LayUi插件

文档&#xff1a;日期和时间组件文档 - Layui layDate安装 npm install layui-laydate...

C++_核心编程_多态案例二-制作饮品

#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为&#xff1a;煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例&#xff0c;提供抽象制作饮品基类&#xff0c;提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者

抖音增长新引擎&#xff1a;品融电商&#xff0c;一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中&#xff0c;品牌如何破浪前行&#xff1f;自建团队成本高、效果难控&#xff1b;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

(二)原型模式

原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日&#xff0c;2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席&#xff0c;并作《智能体在安全领域的应用实践》主题演讲&#xff0c;分享了在智能体在安全领域的突破性实践。他指出&#xff0c;百度通过将安全能力…...

安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲

文章目录 前言第一部分&#xff1a;体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分&#xff1a;体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...

MySQL的pymysql操作

本章是MySQL的最后一章&#xff0c;MySQL到此完结&#xff0c;下一站Hadoop&#xff01;&#xff01;&#xff01; 这章很简单&#xff0c;完整代码在最后&#xff0c;详细讲解之前python课程里面也有&#xff0c;感兴趣的可以往前找一下 一、查询操作 我们需要打开pycharm …...

uni-app学习笔记三十五--扩展组件的安装和使用

由于内置组件不能满足日常开发需要&#xff0c;uniapp官方也提供了众多的扩展组件供我们使用。由于不是内置组件&#xff0c;需要安装才能使用。 一、安装扩展插件 安装方法&#xff1a; 1.访问uniapp官方文档组件部分&#xff1a;组件使用的入门教程 | uni-app官网 点击左侧…...

Java后端检查空条件查询

通过抛出运行异常&#xff1a;throw new RuntimeException("请输入查询条件&#xff01;");BranchWarehouseServiceImpl.java // 查询试剂交易&#xff08;入库/出库&#xff09;记录Overridepublic List<BranchWarehouseTransactions> queryForReagent(Branch…...

向量几何的二元性:叉乘模长与内积投影的深层联系

在数学与物理的空间世界中&#xff0c;向量运算构成了理解几何结构的基石。叉乘&#xff08;外积&#xff09;与点积&#xff08;内积&#xff09;作为向量代数的两大支柱&#xff0c;表面上呈现出截然不同的几何意义与代数形式&#xff0c;却在深层次上揭示了向量间相互作用的…...

归并排序:分治思想的高效排序

目录 基本原理 流程图解 实现方法 递归实现 非递归实现 演示过程 时间复杂度 基本原理 归并排序(Merge Sort)是一种基于分治思想的排序算法&#xff0c;由约翰冯诺伊曼在1945年提出。其核心思想包括&#xff1a; 分割(Divide)&#xff1a;将待排序数组递归地分成两个子…...