当前位置: 首页
编程语言
iOS按钮从点击到响应完整流程原理图文详解

iOS按钮从点击到响应完整流程原理图文详解

热心网友 时间:2026-06-14
转载

触摸屏事件经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 提供了更高级的封装:UIGestureRecognizerUIControl

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 的子类。

保持前面修正的流程:

  • 如果响应者是 UIButtonUISwitchUISlider 等系统控件(即 UIControl 系统子类),则直接执行 target-action,响应者不再接收 touchBegan 等事件
  • target-action 的执行流程:响应者调用 sendAction 转发给 Application,Application 调用 sendAction 分发到指定 target
  • 若未指定 target,则将事件分发到响应链上第一个希望处理的对象

UIControl 还提供了自定义行为的接口:

  • beginTrackingWithTouch
  • continueTrackingWithTouch
  • endTrackingWithTouch
  • cancelTrackingWithEvent

以上,就是 iOS Button 响应流程的图文详解。理解这些底层细节,对于调试 UI 问题、自定义控件响应逻辑都很有帮助。

来源:https://www.jb51.net/article/268390.htm

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

同类文章
更多
AWS RDS 数据库配置入门与基础操作指南

AWS RDS 数据库配置入门与基础操作指南

本文介绍了AWSRDS的基本概念与核心价值,即提供托管式关系数据库服务,简化运维。详细阐述了创建RDS实例的关键配置步骤,包括引擎选择、实例规格、存储与网络设置。最后,指导读者如何通过多种方式安全连接至数据库实例,并开始进行数据操作,为后续应用开发奠定基础。

时间:2026-07-10 06:41
PHP MVC中AJAX请求无法调用控制器方法的原因与解决方案

PHP MVC中AJAX请求无法调用控制器方法的原因与解决方案

PHPMVC中AJAX请求返回整页HTML的常见原因是控制器方法未正确输出响应或未终止执行,导致框架渲染视图。解决方法是在控制器中设置JSON响应头、输出数据后调用exit()明确终止,同时前端使用小写url和dataType: "json "。

时间:2026-07-10 06:40
Go语言手动构造rsa.PublicKey:正确初始化大整数模数N完整指南

Go语言手动构造rsa.PublicKey:正确初始化大整数模数N完整指南

手动构造RSA公钥时,模数N为*big Int类型,不能直接使用超长十进制字面量,需通过SetString或UnmarshalText方法解析字符串。公钥指数E可直接赋值,推荐65537。生产环境应使用rsa GenerateKey生成密钥对,避免手动构造引发的安全和格式错误。

时间:2026-07-10 06:39
Go语言实现HTTP定时轮询监控多URL响应时间与状态检测

Go语言实现HTTP定时轮询监控多URL响应时间与状态检测

使用Go语言实现HTTP定时轮询监控,通过按行分割与Tab解析URL列表,避免闭包陷阱和nil指针,每个URL启动独立ticker安全并发请求,并配置超时控制与资源关闭,确保响应时间与状态码准确检测。

时间:2026-07-10 06:39
Tkinter中Label标签在主循环动态更新的正确方法

Tkinter中Label标签在主循环动态更新的正确方法

在Tkinter中正确动态更新标签的方法:将标签组件的textvariable参数绑定到一个StringVar变量,然后通过调用该变量的 set()方法更新其值,界面会自动刷新。这样避免直接修改text属性或调用update()。此做法实现数据与界面的解耦,代码更简洁,响应更及时,避免手动同步的闪烁,推荐做法。

时间:2026-07-10 06:39
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全
  • 热门数据榜