一场关于 SwiftUI 动画的“原生”之争 -- 肘子的 Swift 周报 #154

作者:东坡肘子日期:2026/9/22

一场关于 SwiftUI 动画的“原生”之争

上周,React Native 核心开发者 Krzysztof Magiera 的一条 ⁠推文 在苹果开发生态中引发热烈讨论。推文中他展示了一个示例:在主线程阻塞的情况下,UIKit 的一个往返动画仍能正常执行,而 SwiftUI 的动画则停滞了。他认为原因在于 SwiftUI 没有使用 Core Animation 的 render-server animation,而是在应用进程中推进动画,因此对 SwiftUI 是否足够“原生”提出了质疑;随后又表示,在 React Native 中,开发者仍可通过 Core Animation 创建不受主线程阻塞影响的动画。

SwiftUI 共同创造者、早期核心开发者 Kyle Macomber 对此回应了不同的 ⁠观点。他解释说,SwiftUI 在设计之初就有意放弃了 Server-side Animation(由 Core Animation 在 render server 中推进动画),而选择让动画值在 client / app process 中计算和推进,以换取更好的可中断性与交互性。这并非技术上的退步,而是一项设计取舍(SwiftUI 并没有放弃 Core Animation,它仍然参与后续的渲染过程,改变的只是动画由谁来推进)。

动画的易用性一直是 SwiftUI 的标志性特色之一,近年来不断完善的弹簧动画,又进一步提升了动画衔接的自然程度和交互表现力。作为一个响应式框架,把动画推进放在 client 端,我认为完全可以理解。当然,代价也很明确:正如 Krzysztof 的示例所示,当动画推进依赖应用进程时,它更容易受到主线程繁忙的影响,也会占用更多 client 端的计算资源。

不过,client-side animation 并不意味着所有插值计算都必须发生在主线程。随着 SwiftUI 不断演进,部分动画以及 Shape 等计算密集型工作已经可以移到后台线程,以减轻主线程的压力。不过,由于动画的推进仍然属于应用进程,异步计算也受到状态更新和渲染流程等条件的限制,因此当主线程被完全阻塞时,动画依然可能停滞。尽管存在这种“弊端”,但从 iOS 18 开始,UIKit 和 AppKit 也开始采纳这套 client-side animation 模型:新的动画 API 可以直接使用 SwiftUI 的 Animation,并由同一套动画基础设施驱动,而不再生成传统的 CAAnimation。

本次讨论呈现出一个有趣的场景:表面上在争论 SwiftUI 是否“原生”,实际展现的却是两种框架设计理念的差异。

作为苹果面向未来的 UI 框架,SwiftUI 在设计时选择走出既有路径,放弃对 Core Animation render-server animation 模型的依赖,以换取更灵活、更强大的动画表达能力。事实上,Core Animation 的创造者 John Harper 本人也是 SwiftUI 的共同创造者之一,这项选择显然不是因为不了解 Core Animation,而是在理解其优势与局限之后做出的架构决定。这还带来了一个隐形优势:SwiftUI 的部分核心能力与苹果平台的具体实现得以进一步解耦。尽管苹果并未将 SwiftUI 开源,但从架构上看,它的一些核心思想并不天然局限于苹果平台。

而从 React Native 的角度看,Krzysztof 的表述也有其合理性。作为一个跨平台框架,React Native 的重要目标之一,就是在不同平台上尽可能利用平台已经提供的能力,让应用在行为和性能上更像“本地人”。

一个最初围绕“什么才算原生”的讨论,最终引出了两边核心开发者对 UI 动画架构的理解与阐述。作为一个吃瓜群众,我也从中收获不少。

本期内容 | 前一期内容 | 全部周报列表

原创

从 pbxproj 到 xcproj:Xcode 工程配置迎来 JSON 格式

最近一直在等待 Xcode 27.1 的到来,以便在 iPhone Duo 的模拟器中测试一下应用的适配情况。有意思的是,27.1 没等到,苹果率先发布了 27.2 beta。尽管 iPhone Duo 模拟器依然缺席,但新增的 project.xcproj 令人眼前一亮。它替代的是 .xcodeproj 包里的那份构建图 project.pbxproj,而不是整个工程包。它有哪些亮点,解决了什么问题,又有哪些事情没有改变,本文将对此进行探讨。

近期推荐

为 Devin 构建云上 Mac 开发环境 (How we built Devin’s Mac)

当 AI Agent 在本地运行时,权限早已授予,工具链已经就绪,遇到弹窗也可以人工干预。搬到云端后,这些理所当然的条件都需要重新构建。换来的好处并不在于能做到本地做不到的事,而是让开发环境本身成为可以分配给 Agent 的资源:任务可以异步运行、彼此隔离,闲置时暂停并保存状态,需要时再恢复,也更容易支持多个 Agent 并行工作。Jon Kelley 在文中介绍了为 Devin 构建 macOS 云端环境的过程,涵盖基于 Virtualization.framework 的虚拟机、磁盘快照、网络隔离、环境与权限预配置、桌面与 Simulator 的实时串流,以及借助无障碍树实现的计算机操作。最后一点对苹果开发者尤其有启发:做好无障碍支持,也是在为 Agent 的自动化验证提供结构化接口。


iOS 27: CrashReportExtension Framework

传统的崩溃处理有一个棘手的问题:当 signal handler 开始工作时,它所在的进程本身可能已经处于不可靠状态,内存可能损坏、锁可能没有释放,很多平时使用的 API 也无法安全调用。iOS 27 新增的 CrashReportExtension 改变了这一执行模型。当 App 崩溃后,系统会启动一个独立的 Extension,并通过只读的 CrashedProcess 让它检查已经崩溃的进程。

Anton Gubarenko 在文中完整演示了这个扩展的创建与使用,也给出了几条务实的判断。该 API 是对系统 .ips 报告的补充而非替代,适合需要自有后端、业务上下文关联或设备端预分析的团队。扩展本身应尽量精简,只负责生成并持久化报告,上传、重试等工作交给主应用,两者通过 App Group 共享存储。对多数独立开发者而言,系统崩溃报告加上 MetricKit 依然足够。


SwiftUI 动画的运作机制 (How SwiftUI animation works)

对于动画,开发者只需改变状态并设置动画函数,剩下的工作便交给 SwiftUI。但这些声明最终是如何转化成屏幕上一帧帧连续变化的画面?Kyle Ye 从 Transaction 和 AttributeGraph 出发,结合 OpenSwiftUI 的开发经验,逐步分析 SwiftUI 如何创建动画上下文、计算中间状态,并在动画过程中持续更新视图与布局。

与将动画插值主要交给 render server 的传统方式不同,SwiftUI 的许多动画需要在应用进程中持续推进。由于插值发生在视图图内部,任何满足 Animatable 的值都能被采样,布局、Shape 等更多状态因此得以参与动画,而 render server 主要处理图层属性。文中对异步提交路径中 CABasicAnimation 的分析尤其值得一读:它只承载当前帧的采样结果,下一帧便会被替换——屏幕上出现了 CA 动画对象,并不意味着动画的时钟已经交给了 Core Animation。这也为本期周评中提到的 SwiftUI 动画是否为“原生”之争提供了更多细节。


为 iPhone Duo 准备的新 API (SwiftUI: Some New APIs/ContainerViews Useful For Duo!)

iPhone Duo 的多种使用形态给 Toolbar 带来了新的挑战。为了充分利用更宽的屏幕并保留纵向内容空间,系统会在部分形态下将原本位于顶部和底部的控件移到屏幕侧边。但传统 Toolbar 中的文本按钮、自定义视图等内容并不一定适合纵向排列,Toolbar 与 Tab Bar 还可能需要争夺有限的侧边空间。iOS 27.1 为此增加了 ToolbarItemAxisBehaviorToolbarVerticalBehaviorToolbarVerticalCompressionBehaviortoolbarVerticalEdge 等 API,让开发者可以进一步控制 Toolbar 的方向、位置以及空间不足时的压缩策略。⁠Itsuki 通过代码和演示展示了这些新 API 的具体行为。

这批 API 的共同特征,是让开发者声明偏好而非指定结果。面对 Duo 带来的多种形态,与其针对特定设备状态编写布局,不如描述内容之间的关系和布局偏好,再由 SwiftUI 根据实际环境决定最终呈现方式。


iPhone Duo 上的 Sheet 与折痕规避 (Sheets and fold avoidance on iPhone Duo)

当 iPhone Duo 处于部分折叠状态时,中央折痕形成了一块不适合交互的区域,按钮等控件如果恰好落在这里,会变得难以操作。⁠Sarun Wongpatcharapakorn 以 Sheet 为例,展示了同一个界面如何在 Duo 的关闭、展开、横屏、竖屏以及部分折叠等不同形态下调整尺寸、位置和 Toolbar。Sheet、Alert 和菜单等系统组件已经内置折痕规避能力,无需开发者干预。因此对于开发者而言,优先使用系统组件,也意味着可以直接获得这些针对不同设备形态的自适应行为。

工具

Vapor 5 Beta 发布

在 Vapor 诞生十周年、距离 Vapor 4 发布六年半之际,社区终于迎来了 Vapor 5 的首个 Beta 版本。此次几乎是一次彻底重写——历时两年、变更约 49,000 行代码。最核心的改动是彻底告别 EventLoopEventLoopFuture,全面转向 Swift Concurrency,并以结构化并发为基础,NIO 也基本从公开 API 中隐去。

同时,Vapor 5 进一步整合 Swift HTTP Types、Swift Configuration 和 Swift Service Lifecycle,并加入实验性的 VaporMacros,为路由和认证提供更多编译期类型检查。Vapor 5 要求 Swift 6.4,目前仍处于 Beta 阶段。有关更多变化,可以查看 ⁠Tim Condon 的 ⁠What’s New in Vapor 5 Beta


Hinge:命令行调节 Duo 模拟器折叠角度

Xcode 27.1 的 Device Hub 可以调整 iPhone Duo Simulator 的折叠角度(按住 Option 键可见),但 simctldevicectl 并没有提供对应的控制命令。⁠Artem Novichkov 开发的 Hinge 补上了这个缺口,可以通过命令行设置 0–180° 的折叠角度,也支持打开、关闭、半折叠以及连续改变角度等操作。Hinge 还提供了 Agent Skill,让 Coding Agent 可以在测试过程中主动改变 Duo 的折叠状态并验证 App。


Enriched Markdown:不依赖 WebView 的跨平台 Markdown 渲染与编辑方案

在移动端实现 Markdown,常见方案是套一层 WebView。Software Mansion 推出的 Enriched Markdown 选择了一条更彻底的路线:基于各平台的原生文本能力完成 Markdown 渲染与富文本编辑,并可以将编辑后的内容重新输出为干净的 Markdown。

它提供独立的 SwiftUI SDK,基于 TextKit 2 与 md4c 实现 Markdown 渲染,同时也覆盖 React Native、Android 和 Web。相比单纯追求“能显示 Markdown”,Enriched Markdown 更重视原生体验:文本可以正常选择,链接、图片与代码块能够接入系统菜单,并适配 VoiceOver、TalkBack、动态字体和从右到左书写(RTL)。

往期内容

💝 支持与反馈

如果本期周报对你有帮助,请:

  • 👍 点赞 - 让更多开发者看到
  • 💬 评论 - 分享你的看法或问题
  • 🔄 转发 - 帮助同行共同成长

🚀 拓展 Swift 视野


一场关于 SwiftUI 动画的“原生”之争 -- 肘子的 Swift 周报 #154》 是转载文章,点击查看原文


相关推荐


C++入门(3)
小七在进步2026/9/14

在上一篇文章中,我们已经掌握了引用的基本概念与用法。今天继续深入,重点讲解 const 引用、引用与指针的关系,以及 inline 和 nullptr 这两个容易被忽视却十分重要的知识点。 引用 const 引用 引用类型用 const 修饰,我们称之为 const 引用。 这里有一个 int 类型的变量 a,用 const 修饰成了常量。 const int a = 10; 我们给 a 取别名 b,如果这样取: int& b=a;//b是a的引用 编译器就会报错。 这种情况本质上涉及


引擎从不发一条消息:jeeflow 的两类扩展点与消息模块的分工
mldong2026/9/6

二、引擎有两类"伸手"的地方:拦截器和事件 引擎核心是零框架依赖的,但它知道一件事:业务方总要"在流程跑的时候干点自己的事"——发通知、改业务单据状态、按规则算审批人、写代码做决策。这些需求它不自己实现,而是开出两类扩展点让外面插进来: 拦截器(FlowInterceptor):插在流程执行过程中,能看到、甚至能改正在发生的事。 事件(ProcessEventListener):流程走到某个关键点时喊一声,外面的人听到后自己决定要不要做点什么。 这两个接口的长相,朴素到可以各贴一行: //


一个 SKILL.md 拿下 2.3 万 Star:让 Codex/Claude Code 少说废话、先给答案
JavaGuide2026/8/29

用 Coding Agent 改代码,最磨耐心的情况之一,就是它明明答对了,答案却藏在一堆废话里。 你让 Codex 排查一个接口为什么返回 401,它可能先查认证流程,再分析中间件、Token 和 Cookie,顺手提醒你依赖版本也该升级了。全部看完之后,真正要执行的命令只有一条。 任务再长一点,人更容易跟丢。Agent 写了很多,却没说现在做到第几步;测试失败了,也没告诉你先看哪个文件。最后再来一句“希望这能帮到你”,问题还停在原地。 前几天技术群里有朋友分享了一个名字很有意思的项目 i-h


AI编程实战:让 AI 修 Bug,从报错到修复
机构师2026/8/21

文章目录 AI编程实战:让 AI 修 Bug,从报错到修复写在前面一、贴报错的正确姿势二、三个真实案例三、多轮定位技巧四、修完要回归小结与下篇预告小作业 AI编程实战:让 AI 修 Bug,从报错到修复 写在前面 Debug 是 AI 最擅长、也最常用的场景之一,但很多人习惯只贴一半报错,效果大打折扣。这篇讲怎么让 AI 高效修 Bug。 一、贴报错的正确姿势 把三样东西一次给全:完整报错 + 相关代码 + 环境信息。 运行报错,请帮我定位并修复。 - 报错:(粘贴完整


GitHub #1 拆解|它说自己在进化,但 worker 跑的是你本机权限,不是沙箱
苏灿烤鱼2026/8/8

GitHub #1 拆解|它说自己在进化,但 worker 跑的是你本机权限,不是沙箱 让可复用经验留下,让错误改动能回滚。 ⚡️ 30 秒速读:PrimeIntellect-ai/prime-agent 新上榜即登顶 GitHub Trending #1,今日新增 2,271 星。它用 RLM 把上下文当变量,用可执行 Skill 和 Continual Harness 保存可 refine 的经验。亮点:技能可被 IPython 直接调用,会话可后台续跑;风险:官方明确不是安全沙箱,v


Android 面试系列:Retrofit 的 suspend 是如何实现的?
潜龙勿用之化骨龙2026/7/30

今天这个是送分题,但是仍然有很多人不清楚,今天就聊聊这个 比如: interface UserApi { @GET("user") suspend fun getUser(): User } 调用: val user = api.getUser() 这里 Retrofit 并不是同步等待网络结果。 它只是把原来的异步流程转换成了 Kotlin 协程可以理解的挂起恢复流程。所以你不要再withContext 一下。 Retrofit 是怎么知道这是 suspend 方法?


为什么学习 Go?Go 能做什么?
程序员爱钓鱼2026/7/22

Go 编程实战系列(一) 近年来,无论是云计算、容器技术、微服务,还是人工智能基础设施,Go(又称 Golang)几乎都占据着重要位置。从 Docker、Kubernetes 到 Prometheus、Etcd,再到大量互联网公司的后端服务,Go 已经成为现代服务端开发的重要语言之一。 对于刚开始学习编程的人来说,可能会有这样几个问题: Go 为什么越来越流行? Go 和 Java、Python、Rust、C++ 有什么区别? 学 Go 能做哪些项目? 学完 Go 能找到什么工


Cursor安全插件链:代码审计新范式
lideiguo2026/7/13

一、 引言:当AI代码助手成为安全审计的“双刃剑” 核心问题:随着 Cursor、GitHub Copilot 等 AI 代码助手的普及,开发效率大幅提升,但同时也引入了新的安全风险——AI 生成的代码可能存在漏洞,而开发者可能因过度信任而疏于审计。 新范式诞生:“安全插件链”应运而生,它通过将静态代码分析、依赖检查、漏洞扫描等安全工具与 Cursor 等 IDE 深度集成,在代码生成、修改、提交等关键环节自动介入,形成“开发即审计”的闭环。 本文目标:探讨 Cursor 安全插件链的核心


基于MATLAB图像处理的饮料瓶识别与价格显示系统设计与实现
7zcode2026/7/5

摘要:随着图像处理技术在商品识别、智能零售和自动检测领域的广泛应用,传统依靠人工判断饮料种类和价格的方式存在效率低、主观性强、信息统计不便等问题。针对饮料瓶图像识别与价格自动显示需求,本文设计并实现了一套基于MATLAB图像处理的饮料瓶识别与价格显示系统。 项目概览 项目简介 本文设计并实现了一种基于MATLAB的饮料瓶智能识别与价格统计系统。该系统采用计算机视觉技术,结合HSV颜色空间阈值分割和形状特征分析两种识别方法,实现对常见饮料瓶的自动识别与分类。系统首先通过图像预处理获取饮料


spring-ai-starter-mcp-server-webmvc的包冲突
山塘小鱼儿2026/6/27

搭建spring-ai的mcp服务,引用官网的包 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://m

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 聚合阅读