iOS按钮从点击到响应完整流程原理图文详解
触摸屏事件经iOS系统捕捉封装后,通过IPC传递至当前应用,主线程Runloop激活并封装UIEvent。事件发送至Window,递归倒序遍历子视图,经坐标转换和hitTest检测确定响应者。事件再通过touch系列方法或UIGestureRecognizer、UIControl的target-action机制处理,未响应则沿响应链回溯。
引言
先从触摸屏幕说起。要理解Button如何响应触摸,首先要弄清坐标转换的必要性。为什么需要转换?
在UI逻辑中,每个控件的坐标都相对于其父视图定义,而布局本身可能嵌套多层。因此,当手指触摸屏幕时,要判断触摸点落在哪个控件上,必须将所有控件的坐标统一换算到相对于窗口的坐标系。

转换后,坐标变成如下形式——(如图)

虽然这个直观逻辑看起来简单,但实际的命中检测过程是顺着UI的嵌套层级递归进行的——从Window开始,逐级向下倒序遍历子视图。每递归到某一层View,就需要将当前View上的触摸点坐标,转换成子View上的坐标。

简而言之,在检测阶段,每次递归转换坐标,本质上就是在遍历子View时,把Point的参考基点从当前View切换到子View。理解这个参考系切换是核心。
简单梳理流程

- 触摸屏幕
- IOKit.framework 捕捉触摸,封装为 IOHIDEvent 对象
- 通过 IPC(进程间通信)转发给 SpringBoard 进程
- 再通过 IPC 将事件转发给当前活跃进程的 AppDelegate
- App 主线程的 Runloop 通过 Port signal(来自 SpringBoard)检测到 Source1,线程从休眠中被唤醒,Runloop 继续轮询
- Runloop 检测到 Source0(InputSource),封装 UIEvent,并加入当前 Application 的 event 队列
- 事件出队列,通过 sendEvent 发送给 Window
- 具体 Source1 处理事件需严谨说明:
- 检测到 Source1,触发回调 __IOHIDEventSystemClientQueueCallback()
- 触发 Source0 回调 __UIApplicationHandleEventQueuqe(),将 IOHIDEvent 封装为 UIEvent
- 调用 UIApplication 的 sendEvent,将 UIEvent 发送给 Window
- Window 开始查询响应者
- rootViewController.view 按照子视图倒序递归查询
- 通过 pointInside 判断触摸点是否落在当前 View 的 bounds 内
- hitTest:如果触摸点落在 bounds 内,则转换触点坐标为相对于屏幕的坐标点,然后递归倒序遍历子视图的 hitTest 检测
- 子视图数组采用倒序遍历的原因:最后添加的 View 位于层次最上层,先检测它,效率更高
- 检测可能出现 3 种结果:
- 目标响应者被禁用了交互,且不是完全透明、未隐藏,结果为没有响应者(nil)
- View 的某个子视图成为目标响应者
- 当前 View 自身成为目标响应者
- Window 调用 sendTouchesForEvent 将触摸事件发送给上面查询到的响应者;如果响应者为 nil,则后续处理停止
- 执行 touchBegan / touchMoved / touchEnded / touchCancelled 捕获处理
- 回调响应者预先设置的 handleCallback(即 selector),并传递响应者自身作为参数
- 根据 touch 的多种逻辑状态,选择合适的 callback
- 例如按下按钮时背景颜色变化
- 离开按钮时颜色恢复等——各种 touch 事件解释类型,对应执行不同的 callback
- 如果响应者未处理 touch,则事件会沿着响应链反向传递给父视图,直至 Application。即若目标响应者未响应,事件沿传递链回溯到 Application,而 Application 默认不做处理
- 处理结束,App 的 Runloop 进入休眠,等待下次唤醒
apple-touch封装
touchBegan / touchMoved / touchEnded / touchCancelled 是底层方法。Apple 提供了更高级的封装:UIGestureRecognizer 和 UIControl。
UIGestureRecognizer 包含 8 种手势:
- UITapGestureRecognizer – 轻点
- UIPinchGestureRecognizer – 捏合
- UIRotationGestureRecognizer – 旋转
- UISwipeGestureRecognizer – 滑动
- UIPanGestureRecognizer – 拖拽
- UIScreenEdgePanGestureRecognizer – 屏幕边缘拖拽
- UILongPressGestureRecognizer – 长按
- UIHoverGestureRecognizer – 悬停(macOS & iPadOS)
window sendTouchesForEvent 后续流程修正
上面的流程基于底层方式描述。若使用了 Apple 封装的 UIGestureRecognizer,流程需稍作调整。
Window 查询到具体响应者之后:
- Window 调用 sendTouchesForEvent 发送给响应者;同时也会发送给响应者视图绑定的 gestureRecognizers
- 响应者视图的某个 gestureRecognizer 匹配成功,会回调响应者的 touchCancelled 方法,响应者不再接收 touch 事件
- 由于手势互斥,其他 gestureRecognizer 也会收到 touchCancelled 回调,且不再接收 touch 事件
- 识别成功的 gesture 设置的 target-action 被执行
- 否则,继续执行 touchBegan / touchMoved / touchEnded 及后续处理
- 处理结束,App 的 Runloop 进入休眠,等待下次唤醒
还有一些额外配置:
- 识别成功后是否取消其他响应(cancelsTouchesInView),取值 true 或 false
- delaysTouchesBegan:是否在手势识别失败后,才将 touchBegin 事件传递给响应者
- delaysTouchesEnded:是否在手势识别失败后,才将 touchEnded 事件传递给响应者
流程进一步细化
UIControl 是 UIView 的子类。
保持前面修正的流程:
- 如果响应者是
UIButton、UISwitch、UISlider等系统控件(即 UIControl 系统子类),则直接执行 target-action,响应者不再接收 touchBegan 等事件 - target-action 的执行流程:响应者调用 sendAction 转发给 Application,Application 调用 sendAction 分发到指定 target
- 若未指定 target,则将事件分发到响应链上第一个希望处理的对象
UIControl 还提供了自定义行为的接口:
- beginTrackingWithTouch
- continueTrackingWithTouch
- endTrackingWithTouch
- cancelTrackingWithEvent
以上,就是 iOS Button 响应流程的图文详解。理解这些底层细节,对于调试 UI 问题、自定义控件响应逻辑都很有帮助。
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
AWS RDS 数据库配置入门与基础操作指南
本文介绍了AWSRDS的基本概念与核心价值,即提供托管式关系数据库服务,简化运维。详细阐述了创建RDS实例的关键配置步骤,包括引擎选择、实例规格、存储与网络设置。最后,指导读者如何通过多种方式安全连接至数据库实例,并开始进行数据操作,为后续应用开发奠定基础。
PHP MVC中AJAX请求无法调用控制器方法的原因与解决方案
PHPMVC中AJAX请求返回整页HTML的常见原因是控制器方法未正确输出响应或未终止执行,导致框架渲染视图。解决方法是在控制器中设置JSON响应头、输出数据后调用exit()明确终止,同时前端使用小写url和dataType: "json "。
Go语言手动构造rsa.PublicKey:正确初始化大整数模数N完整指南
手动构造RSA公钥时,模数N为*big Int类型,不能直接使用超长十进制字面量,需通过SetString或UnmarshalText方法解析字符串。公钥指数E可直接赋值,推荐65537。生产环境应使用rsa GenerateKey生成密钥对,避免手动构造引发的安全和格式错误。
Go语言实现HTTP定时轮询监控多URL响应时间与状态检测
使用Go语言实现HTTP定时轮询监控,通过按行分割与Tab解析URL列表,避免闭包陷阱和nil指针,每个URL启动独立ticker安全并发请求,并配置超时控制与资源关闭,确保响应时间与状态码准确检测。
Tkinter中Label标签在主循环动态更新的正确方法
在Tkinter中正确动态更新标签的方法:将标签组件的textvariable参数绑定到一个StringVar变量,然后通过调用该变量的 set()方法更新其值,界面会自动刷新。这样避免直接修改text属性或调用update()。此做法实现数据与界面的解耦,代码更简洁,响应更及时,避免手动同步的闪烁,推荐做法。
- 热门数据榜
相关攻略
2026-07-10 06:41
2026-07-10 06:40
2026-07-10 06:39
2026-07-10 06:39
2026-07-10 06:39
2026-07-10 06:39
2026-07-10 06:39
2026-07-10 06:39
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程

