阅读视图

发现新文章,点击刷新页面。

AT 的人生未必比 MT 更好 - 肘子的 Swift 周报 #118

学车时我开的是手动挡,起初因为技术生疏,常搞得手忙脚乱,所以第一台车就直接选了自动挡。但开了几年,我开始追求那种完全掌控的驾驶感,于是又增购了一台手动挡。遗憾的是,随着交通日益拥堵,换挡的乐趣逐渐被疲惫抵消,最终这台车也被冷落。算起来,我已经快二十年没认真开过手动挡了,但内心深处,我仍会时不时地怀念那段“人车合一”的时光。

2026:当 AI 隐入工作流,你准备好了吗? -- 肘子的 Swift 周报 #117

issue117.webp

2026:当 AI 隐入工作流,你准备好了吗?

大家新年好!在过去的几年中,AI 始终占据着科技界最耀眼的 C 位。但站在 2026 年的起点回看,我发现一个显著的转折:从 2025 年末开始,人们对“万亿参数”或“榜单跑分”的狂热逐渐褪去,取而代之的是对 AI 工作流深耕细作的冷静与实战。

如果说过去两年大多数人还在尝试如何与 Chat 机器人聊天,那么现在,AI 已经通过 CLI、MCP 以及各种 Slash、Skill、SubAgent,彻底打破了对话框的限制。对于有经验的开发者来说,AI 已经不再是一个外部工具,而是像插件一样,渗透进终端、编辑器乃至整个操作系统的每一个毛细血管。

在这一点上,macOS 展示了某种“无心插柳”的天然优势。借助 AppleScript 和快捷指令这些成熟的自动化工具,即便不通过复杂的 API 开发,普通用户也能让 AI 访问自己的私有数据。这种“老树发新芽”的现象,让苹果在 AI 时代拥有了新的护城河。而如果这种能力在 iOS 上通过系统级 Agent 完全释放,硬件设备的形态或许将迎来新一轮重塑。

与此同时,某些厂商的策略则更加“激进”。字节跳动的豆包手机尝试从系统底层通过屏幕读取与模拟交互来“暴力”接管一切;华为则通过 A2A(Agent to Agent)策略,试图在后台构建一套统一的代理调度机制。无论路线如何,2026 年对于普通消费者来说都标志着一个奇点的到来:AI 不再是聊天工具,而是显式或隐式地接管了我们的数字生活。

正如那句老话:当一个技术不再被反复提及,才说明它已真正融入生活,如同血液般不可或缺。

然而,越是无感,越要警惕。当 AI 深入工作流的每一个细节,隐私将成为最昂贵的奢侈品。在追求极致自动化与效率的同时,如何选择服务商、如何平衡本地与云端模型、如何保留最后一点象征性的“隐私”,将是我们在 2026 年必须面对的命题。

2026 来了,你开始将 AI 集成到自己的工作流中了吗?

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

🚀 《肘子的 Swift 周报》

每周为你精选最值得关注的 Swift、SwiftUI 技术动态

近期推荐

独立开发者的试炼:Zipic 从 0 到 1 的产品化之路

Zipic 是我一直在高频使用的图片压缩工具,我亲眼见证了这个应用如何从一个职场工作的小需求,逐渐在作者 十里 的不断打磨下成长为一个高效、精致、专注的成功产品。独立开发者往往意味着“一人成军”,时刻在策略、设计、开发、分发与推广之间来回切换。为了挖掘这背后的故事,我邀请了十里复盘了 Zipic 从 0 到 1 的全过程。全文共分三个篇章:产品设计(本文)、不依赖 Mac App Store 的分发与售卖 以及 技术细节复盘:SwiftUI 痛点与性能瓶颈


Swift vs. Rust:从内存管理的终极对决中学到的 5 个惊人事实

在开发者社区中,关于 Swift 和 Rust 性能的讨论从未停止。通常的看法是:Swift 因为自动引用计数(ARC)而相对较慢,而 Rust 则以其极致的速度和内存效率著称。但 Snow 认为,这种“快”与“慢”的简单标签往往掩盖了两者在设计哲学上的根本差异:Swift 优先开发体验和生态兼容,Rust 追求极致性能和编译时安全。

结合实际案例,文章揭示了五个真相:Rust 的所有权规则本质上是零开销的编译时工具;Swift 的真正性能包袱来自 Objective-C 兼容性而非 ARC 本身;ARC 的核心问题是性能的不可预测性;并发安全上 Swift 依赖运行时保护而 Rust 实现编译时保证;以及为何 Swift 无法“变成”Rust。


StoreKit 订阅实战指南 (StoreKit Subscriptions: A Practical Guide)

Mohammad Azam 基于多年 iOS 开发经验和真实案例,撰写了完整的 StoreKit 订阅实践教程。系列涵盖:变现模型选择(一次性购买、订阅、消耗型购买及混合策略)、付费墙策略对比(软/硬付费墙及订阅试用的权衡)、引导体验设计(从静态截图演进到 8 步交互式引导,让用户在付费前完成核心功能体验并建立情感投入)、以及完整的技术实现(App Store Connect 配置、StoreKit 集成、产品加载和购买流程的代码示例)。


Skip 2025 回顾与 2026 路线图 (Skip 2025 Retrospective and 2026 Roadmap)

在 2025 年,随着 Swift SDK for Android 在 swift.org 正式发布,Skip 通过 Skip Fuse 提供原生编译支持,解锁了数千个原生 Swift 包在 Android 上的使用。同时新增 NFC、Stripe、PostHog、Auth0、Socket.IO 等双平台框架。iOS 26 推出的 Liquid Glass 界面风格成为跨平台框架的试金石。Skip 因采用“完全原生”策略(iOS 上使用原生 SwiftUI,Android 上映射到 Jetpack Compose)而在第一天就自动支持新界面,无需重写或变通。在 2026 年 Skip 计划继续扩展集成框架、优化 Skip Fuse 工具链、提升性能和开发体验。


如何使用 Claude Code (How to use Claude Code)

这是一份 Khoa Pham 在高强度使用 Claude Code 数月后整理的实战指南。核心技巧包括各种不同模式的详细应用场景,尤其是如何合理使用 Extended Thinking 模式以避免浪费 Token。另外还涵盖了关键快捷键、上下文管理技巧、MCP 集成、VS Code 和 Chrome 扩展、GitHub Actions 集成、Git Worktrees 并行工作流、插件生态以及提示词最佳实践等众多内容。内容详实、具体、有针对性,并非简单的功能介绍手册。


App Store Connect API Webhook 串接|提升 iOS CI/CD 自动化效率与通知流程

苹果在 WWDC 2025 中发布了 App Store Connect API Webhook,支持构建状态、App 版本状态、TestFlight 反馈等事件的实时推送。Zhong Cheng 针对打包上传后传统 Polling 方式需等待约 20 分钟(GitHub Runner 浪费 $1.24/次)的痛点,详细介绍了如何在 CI/CD 中应用该能力,实现零等待成本;GitFlow 回 master 时机可精确对齐 App 实际发布时间;开发者权限受限时也能及时收到拒审通知。


使用 WendyOS 开发嵌入式 Linux 应用 (Setting up Embedded Linux with WendyOS)

WendyOS 是一个专为嵌入式设备设计的 Linux 发行版,用 Swift 编写,旨在将 iOS 开发的便捷性带到嵌入式领域。Joannis Orlandos 在本文中提供了完整上手教程:从安装 Homebrew 和 Wendy 工具、刷写 WendyOS 到树莓派/Jetson Orin Nano 等设备、通过 USB 连接设备、配置 WiFi、创建 Swift 项目(含 wendy.json 权限配置)到使用 VSCode 扩展进行远程调试(支持断点和状态检查)。适合想将 Swift 应用到嵌入式设备或 IoT 场景的开发者作为入门教程。

工具

Swift 并发:通俗易懂的学习指南 (Fucking Approachable Swift Concurrency)

这是一个由 Pedro Piñera 创建、基于 Matt Massicotte 的 Swift 并发理念整理的学习资源,用通俗易懂的方式解释 async/await、Task、Actor、Sendable 等核心概念。Pedro 通过 "Office Building(办公楼)" 这一场景,将 MainActor 比作前台、actor 比作部门办公室、await 比作敲门等待,帮助开发者建立直观的心智模型。 另外,还提供了一个适用于 AI 工具的 Skill.md 文件,方便开发者将上述并发实践直接嵌入到开发工作流的规则引擎中。


Dimillian's Skills - iOS/Swift 开发 AI Agent Skills 集合

Thomas Ricouard 创建的用于 iOS/Swift 开发的 Skills 仓库,包含六个专注于实际工作流的 AI Agent Skills。涵盖 App Store Changelog 生成(从 git history 自动生成发布说明)、iOS Debugger Agent(使用 XcodeBuildMCP 构建/调试 iOS 项目)、Swift Concurrency Expert(修复 Swift 6.2 并发问题)、SwiftUI Liquid Glass(实现 iOS 26+ Liquid Glass API)、SwiftUI View Refactor(重构视图结构和依赖模式)、SwiftUI Performance Audit(审查性能瓶颈并提供优化建议)等。


StoreKit Helper - SwiftUI 应用内购买封装库

jaywcjlove开发的轻量级 StoreKit 2 封装库,专为 SwiftUI 设计,大幅简化应用内购买实现。相比直接使用 StoreKit 2 API,StoreKitHelper 减少了约 70% 的样板代码,特别适合需要快速集成应用内购买且不想处理底层复杂性的 SwiftUI 开发者。

核心特性包括:基于 @ObservableObject 的状态管理、协议驱动的类型安全产品定义、实时交易监听和自动状态更新、内置的 StoreKitHelperViewStoreKitHelperSelectionView UI 组件。通过 hasNotPurchased/hasPurchased 属性可以轻松控制界面显示,支持链式 API 配置购买弹窗的各种回调。

求贤

Mac OS 工程师

Photon 正在构建开源基础设施,帮助开发者将 AI Agent 带到人类已经熟悉的交互界面中,例如 iMessage、WhatsApp、电话通话、Discord、Signal 等。在此之上,我们还在打造以交互为核心的开源 Agent SDK,覆盖多段消息处理、消息线程处理、表情/回应(Tapbacks)等能力,让开发者和企业能够开发真正"像人一样"交流的 Agent。

职位要求

我们正在招聘 macOS 工程师,理想的候选人应具备以下条件:

  • 对 macOS 内部机制以及系统组件之间的交互有深入理解
  • 具备 macOS 系统分析与调试经验
  • 熟悉 macOS 系统级 API 及底层机制
  • 对探索 Apple 服务中的未知部分有好奇心
  • 加分项:有 iMessage、IMAgent 或相关消息基础设施的经验

薪资待遇

我们将提供具有竞争力的薪资(工作地点:美国,支持远程办公)。此外,Photon 获得多家知名投资机构的支持。

联系方式

ryan@photon.codes

这是朋友创业团队 Photon 的招聘。他们在做 AI Agent 在 iMessage/WhatsApp 等平台的基础设施,是个早期项目。如果你对 macOS 底层技术和早期创业机会感兴趣,可以了解一下。

往期内容

💝 支持与反馈

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

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

🚀 拓展 Swift 视野

Swift、SwiftUI 与 SwiftData:走向成熟的 2025 -- 肘子的 Swift 周报 #116

issue116.webp

Swift、SwiftUI 与 SwiftData:走向成熟的 2025

在过去的几天里,我回顾了这一年来 Swift、SwiftUI 以及 SwiftData 的演进。总的感觉是:惊喜虽不算多,但“成熟感”却在不经意间扑面而来。

毋庸置疑,Swift 今年的重头戏在于改善并发编程的体验。尽管新增的选项和关键字在短期内又给开发者带来了不小的困扰,但经过这几个月的讨论与实践,社区已经显现出逐渐总结出新范式实践路径的趋势。我不认为新范式被确立且广泛接受会是一个简单、迅速的过程,但或许再过一两年,开发者对 Swift 的讨论重心将从并发转向跨平台,届时 Swift 也将迈入全新的发展阶段。

今年 SwiftUI 的更新重心大多集中在 Liquid Glass 的适配上。受限于系统初期的实现,显示效果起初并不尽如人意,但在 iOS 26.2 版本发布后,性能与稳定性都有了显著改善。坦率地说,对于今年 SwiftUI 没有引入更多革命性的新功能,我个人是挺高兴的。这让框架团队和开发者都能获得一点喘息之机,去进一步消化这个框架。在现阶段,解决遗留问题、优化性能与稳定性,远比一味堆砌新特性更有意义。

“变化较小”在 SwiftData 身上体现得尤为明显。但我认为 SwiftData 今年的表现尤为值得肯定,特别是许多改进与新功能都向下适配到了更早的系统版本。真希望它在三年前初次发布时,就能具备现在的状态。尽管 SwiftData 目前仍缺失一些关键功能,但对于相当比例的项目而言,它已经足以胜任。有了这个稳固的基础,其未来几年在性能与功能上的提高非常值得期待。

对于 2025 年 Swift 三件套的交出的答卷,我个人是满意的,不知你的感受如何?

这是本年度的最后一期周报,由衷感谢各位一年的陪伴与厚爱。

祝大家新年快乐,Happy Coding!

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

🚀 《肘子的 Swift 周报》

每周为你精选最值得关注的 Swift、SwiftUI 技术动态

活动

iOS Conf SG 2026

下个月(1 月 21 日 - 23 日),iOS Conf SG 将在新加坡举行。我也将前往现场,并作为嘉宾进行主题为 “Using SwiftUI as a Language” 的演讲——不仅关于代码,更是关于思维方式的转换。

如果你也在附近,或者计划前往,欢迎来现场打招呼!组委会专门为我的读者提供了优惠:Fatbobman 读者专属九折优惠链接

近期推荐

我和 CloudKit 的这八年:从开源 IceCream 到商业应用实战

我一直认为,所谓的苹果生态是由很多的硬件、软件、服务、人文、气质等综合构建起来的。在这其中,CloudKit 无疑是非常重要的一环。而且对于开发者来说,用好 CloudKit 不仅可以给用户更好的体验,也能低成本的为自己的应用带来创新。

IceCream 作者 Cai Yue 分享他与 CloudKit 八年的开发历程:从 2017 年开源 IceCream 并获得 Apple 官方认可,到将 CloudKit 应用于 Music Mate 和 Setlists 等商业项目的实战经验。文章深入探讨了 CloudKit 的核心优势、关键局限以及进阶玩法。


Swift 2025 年度总结 (What's new in Swift: December 2025 Edition)

这是一篇面向 Swift 社区的年度收官综述文章,由 Tim SneathDave Lester 撰写,系统回顾了 2025 年 Swift 生态在语言特性、平台覆盖与社区建设方面的关键进展。

文章不仅总结了 Swift 6.2 在并发模型上通过更温和的默认策略降低使用门槛,同时继续推进 C++ 互操作与内存安全能力;更重要的是,从 Android、WASM、Windows、BSD、嵌入式到 AWS 等方向的持续投入,反复强化了一个清晰信号——Swift 已不再只是围绕 Apple 平台展开的语言。

或许你未必会认同其中的每一项变化,但在迈入第二个十年后的第一个年头里,Swift 依然交出了一份相当扎实的答卷。


关于 SwiftUI 的讨论 (My PM insisted we switch to SwiftUI for a massive legacy app rewrite. The result is exactly what you'd expect)

几天前无意间在 Reddit 上看到的帖子,作者对 PM 轻易选择 SwiftUI 有所抱怨,认为其无法胜任他们一个七年前开发的应用转换。对于这个观点我不置可否,但评论区的走向却出乎意料——绝大多数参与者都坚定地站在了 SwiftUI 的一边。

大量开发者认为:

  • SwiftUI 本身已经足够成熟,问题出在实施方式上
  • 应该渐进式迁移,而不是一次性重写
  • 避开 SwiftUI 的弱项——比如可以保留 UIKit 导航,只迁移视图层
  • 多个大型项目(10+ 年历史)已成功完成迁移

这个帖子展现了一个出乎我预料的现实:SwiftUI 在实际生产环境中的采用率比我们想象的高得多;开发者社区对 SwiftUI 的信心已经建立。在 2025 年底,“SwiftUI 难堪大任”的论调或许已经站不住脚了。

作为 SwiftUI 框架的推崇者,我既喜欢该框架,也很清楚它仍有很长的路要走。如果你仍在犹豫是否应该在 SwiftUI 上下功夫,或许可以看一下我在去年写的《几个常见的关于 SwiftUI 的误解》——这篇文章讨论的很多误解,恰好在这次 Reddit 讨论中得到了印证。


非 Sendable 优先设计 (Non-Sendable First Design)

随着 Swift 6 时代的到来,开发者逐渐养成了一种惯性:要么让类型符合 Sendable,要么给它套上 @MainActoractor。在这篇文章中,Matt Massicotte 提出了一个极具启发性的哲学:“非 Sendable 优先设计”

这一思路的关键在于对“隔离(Isolation)”的重新认识:隔离本身是一种约束。当一个类型被标记为 @MainActor,它实际上就失去了在非 UI 环境下进行同步调用的自由度。相比之下,一个非隔离、非 Sendable 的普通类型反而具有更高的通用性——它可以被任意 Actor 持有,并在其内部安全地进行同步访问,同时也更容易遵循 Equatable 等基础协议,而无需处理跨隔离域带来的复杂性。

随着 Swift 引入 NonisolatedNonsendingByDefault,这种“非 Sendable 优先”的设计路径不再像过去那样笨重或别扭,反而逐渐显现出其优势:以更少的隔离、换取更清晰的语义与更低的架构负担。这或许并非适用于所有场景,但在 Swift 6 之后,它已经成为一种值得认真考虑的、符合语言直觉的“减法”方案。


使用 Registry 加速依赖解析 (Resolving Swift Packages faster With Registry from Tuist)

传统的 SPM 依赖解析是基于 Git URL 的,Xcode 需要克隆整个 Git 仓库来获取版本信息和代码,这在依赖较多(如 Firebase)时非常耗时。而 Registry 是苹果定义的另一种规范:通过包的标识符(ID)直接下载特定版本的归档文件,跳过了繁重的 Git 操作。Tuist 最近宣布将其 Swift Package Registry 功能向所有开发者开放,最大的变化是现在无需登录或创建 Tuist 账号即可使用。

Lee Young-jun 实测发现,使用 Registry 后,依赖解析(Installation)时间缩短至原来的约 35%;但项目生成与构建阶段并未获得同等收益,甚至略有回退。在 GitHub Actions 中配合缓存使用时,二次构建的依赖安装时间则从 53s 降至 11s,优势主要体现在 CI 场景。

总体来看,Tuist Registry 并非“全流程加速器”,而是一个专注于依赖解析与缓存友好性的优化点。如果你的项目依赖数量庞大、CI 成本较高,它值得优先尝试。


iOS Timer 与 DispatchSourceTimer 选择与安全封装技巧|有限状态机防止闪退

很多开发者在处理 DispatchSourceTimer 时,最头疼的就是它那“易碎”的状态:调用顺序稍有不对便会引发闪退。ZhgChgLi 在本文中针对这种极其敏感的状态管理提出了工程化的解决方案。文章详尽列举了导致崩溃的五大常见场景(如重复 resume、suspend 状态下直接释放等),并分享了如何利用有限状态机 (FSM) 封装操作,从逻辑层屏蔽非法调用,同时配合私有串行队列确保多线程环境下的调用安全。

这是一篇引导读者从“写代码”转向“做设计”的实战案例。它不仅讲清了 GCD 定时器的正确使用方式,更展示了如何借助设计模式,将一个“危险”的底层 API,封装为语义清晰、使用安全、可长期维护的工业级组件。在 Swift Concurrency 日益成为主流的今天,理解并优雅地封装这些底层 GCD 工具,依然是高级 iOS 开发者的重要基本功。

工具

ml-sharp:照片秒变 3D 场景

苹果在上周开源了 SHARP (Sharp Monocular View Synthesis),一个能在不到 1 秒内将单张 2D 照片转换为 3D 场景的 AI 模型(模型大小 2.8 GB)。相比之前的最佳模型,视觉质量提升 25-34%,速度提升 1000 倍。

社区普遍认为 SHARP 可能用于未来版本的空间照片功能。目前 iOS 26 的 Spatial Scenes 使用 Neural Engine 进行深度重建,而 SHARP 采用更先进的 3D Gaussian Splatting 技术,质量显著提升。

模型支持 CPU/CUDA/MPS 运行,已有开发者在 M1/M2/M3 Mac 上成功运行。输出的 .ply 文件兼容各种 3DGS 查看器,Vision Pro 用户可通过 Metal Splatter 直接查看效果

尽管苹果在通用语言大模型上不如竞争对手惊艳,但在垂直场景的 AI 模型上,凭借硬件深度整合与明确的应用导向,依然展现出强大的竞争力。


MaterialView: 突破 NSVisualEffectView 限制的毛玻璃视图

Oskar Groth (Sensei 作者)开源了 MaterialView,一个能够突破 NSVisualEffectView 限制的高度可定制毛玻璃视图库。通过逆向 Control Center 的实现,Oskar 实现了对模糊半径、饱和度、亮度和色调的完全控制,并撰写了详细的技术文章讲解实现原理。

与系统原生材质只能“选类型”不同,MaterialView 将模糊效果彻底参数化,允许开发者精确控制模糊半径、饱和度、亮度、tint 颜色与混合模式,并支持 active / inactive / emphasized / accessibility 等状态配置。这使得它非常适合用于侧边栏、浮层面板、工具窗口等对视觉一致性要求极高的场景。

该库同时支持 SwiftUI 与 AppKit,并提供了一个可实时调参的 Demo App,方便快速探索不同材质组合的效果。

需要注意的是,它依赖部分未公开的 Core Animation 能力(如 CABackdropLayerCAFilter 等)。尽管这些 API 多年来相当稳定,但仍存在未来系统版本变动的潜在风险。

materialview-demo.gif

往期内容

💝 支持与反馈

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

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

🚀 拓展 Swift 视野

SwiftUI 中的 compositingGroup():真正含义与渲染原理

在学习 SwiftUI 的过程中,很多人第一次看到 compositingGroup() 都会被官方文档这句话绕晕:

Use compositingGroup() to apply effects to a parent view before applying effects to this view.

“让父 View 的效果先于子 View 的效果生效”  —— 这句话如果按字面理解,几乎一定会误解。

本文将从 渲染顺序、效果作用范围、实际示例 三个角度,彻底讲清楚 compositingGroup() 到底解决了什么问题。


一句话结论(先记住)

compositingGroup() 会创建一个“合成边界”:

  • 没有它:父 View 的合成效果会被「拆分」并逐个作用到子 View
  • 有了它:子 View 会先整体合成,再统一应用父 View 的合成效果

⚠️ 它改变的不是 modifier 的书写顺序,而是“效果的作用范围”。


SwiftUI 默认的渲染行为(最关键)

先看一个最简单的例子:

VStack {
    Text("A")
    Text("B")
}
.opacity(0.5)

看起来是对 VStack 设置了透明度

但 SwiftUI 实际做的是:

Text("A") -> opacity 0.5
Text("B") -> opacity 0.5
再进行叠加

也就是说:

  • opacity 并没有作为一个“整体效果”存在
  • 而是被 拆分后逐个应用到子 View

这就是很多「透明度叠加变脏」「blur 看起来不对劲」的根源。


compositingGroup() 做了什么?

加上 compositingGroup()

VStack {
    Text("A")
    Text("B")
}
.compositingGroup()
.opacity(0.5)

SwiftUI 的渲染流程会变成:

VStack
 ├─ Text("A")
 └─ Text("B")

先合成为一张离屏图像

对这张图像应用 opacity 0.5

关键变化只有一句话

父 View 的合成类效果不再下发到子 View。


那官方说的“父 View 的效果先于子 View 的效果”是什么意思?

这句话并不是时间顺序,而是:

父 View 的合成效果不会参与子 View 的内部计算。

换句话说:

  • 子 View 内部的 blur / color / mask 先完成
  • 父 View 的 opacity / blendMode 再整体生效

而不是交叉、叠加、重复计算。


一个典型示例:blur + opacity

❌ 没有 compositingGroup

ZStack {
    Text("Hello")
    Text("Hello")
        .blur(radius: 5)
}
.opacity(0.5)

实际效果:

  1. 第二个 Text 先 blur
  2. 两个 Text 分别被 opacity 影响
  3. 模糊区域再次参与透明度混合
  4. 结果:画面更糊、更脏

✅ 使用 compositingGroup

ZStack {
    Text("Hello")
    Text("Hello")
        .blur(radius: 5)
}
.compositingGroup()
.opacity(0.5)

渲染流程变为:

  1. 子 View 内部:blur 只影响指定的 Text
  2. ZStack 合成完成
  3. 整体统一 opacity 0.5

📌 blur 不再被“二次污染”


compositingGroup() 常见适用场景

1️⃣ 半透明容器(避免透明度叠加)

VStack {
    ...
}
.compositingGroup()
.opacity(0.8)

2️⃣ blendMode 视觉异常

ZStack {
    ...
}
.compositingGroup()
.blendMode(.multiply)

3️⃣ 动画 + blur / scale / opacity

.content
.compositingGroup()
.transition(.opacity)

可显著减少闪烁、重影问题。


compositingGroup vs drawingGroup

对比项 compositingGroup drawingGroup
是否离屏渲染
是否使用 Metal
主要目的 控制合成效果作用范围 性能 / 特效加速
常见问题 解决视觉叠加 解决复杂绘制性能

📌 compositingGroup 关注“视觉正确性”,drawingGroup 更偏向“性能”。


记忆口诀(非常实用)

要“整体效果”,用 compositingGroup;
不想被子 View 叠加污染,也用 compositingGroup。


总结

  • compositingGroup() 并不会改变 modifier 的书写顺序
  • 它创建了一个 合成边界(compositing boundary)
  • 阻止父 View 的合成效果被拆分并下发到子 View
  • 在 opacity、blur、blendMode、动画场景中极其重要

如果你在 SwiftUI 中遇到:

  • 透明度看起来“不对”
  • blur 过重
  • 动画时出现重影

👉 第一时间就该想到 compositingGroup()


希望这篇文章能帮你真正理解 SwiftUI 背后的渲染逻辑。

SwiftUI 中的 @ViewBuilder 全面解析

SwiftUI 中的 @ViewBuilder 全面解析

在 SwiftUI 的世界里,@ViewBuilder 是一个你每天都在用,却可能从未认真了解过的核心机制

很多 SwiftUI 看起来“像写 DSL 一样优雅”的代码,其实都离不开它。

本文将从为什么需要它、它解决了什么问题、如何使用、常见坑点几个维度,系统性地介绍 @ViewBuilder,适合 SwiftUI 初学者到中级开发者 阅读。


一、问题的起点:Swift 只能返回一个值

在 Swift 中,函数或计算属性只能返回一个值

但在 SwiftUI 中,我们却经常写出这样的代码:

var body: some View {
    Text("Hello")
    Image(systemName: "star")
    Button("Tap") { }
}

表面看起来像是“返回了多个 View”,这在普通 Swift 函数里是不可能的

那 SwiftUI 是怎么做到的?

答案就是: @ViewBuilder


二、@ViewBuilder 是什么

@ViewBuilder 是 Swift 的一种 Result Builder(结果构建器)

它的核心职责只有一个:

把多行 View 表达式,组合成一个 View 返回。

你写的代码是这样:

Text("A")
Text("B")
Text("C")

编译器在背后会帮你组合成类似:

TupleView<(Text, Text, Text)>

但这些具体类型对开发者是隐藏的,你只需要关心:

可以像写布局一样写 View,而不是手动拼装结构。


三、为什么你很少看到 @ViewBuilder

因为 SwiftUI 已经帮你加好了。

例如:

struct ContentView: View {
    var body: some View {
        Text("Hello")
        Text("World")
    }
}

实际上等价于:

struct ContentView: View {
    @ViewBuilder
    var body: some View {
        Text("Hello")
        Text("World")
    }
}

👉 body 天生就支持多 View 与条件语法


四、@ViewBuilder 支持哪些能力

1️⃣ 多个 View

@ViewBuilder
var content: some View {
    Text("Title")
    Text("Subtitle")
}

2️⃣ if / else 条件渲染(非常重要)

没有 @ViewBuilder,下面代码是非法的:

func makeView(flag: Bool) -> some View {
    if flag {
        Text("Yes")
    } else {
        Text("No")
    }
}

使用 @ViewBuilder 后:

@ViewBuilder
func makeView(flag: Bool) -> some View {
    if flag {
        Text("Yes")
    } else {
        Text("No")
    }
}

👉 这正是 SwiftUI 条件 UI 渲染的基础能力


3️⃣ 只有 if(没有 else

@ViewBuilder
var body: some View {
    Text("Always Visible")

    if isLogin {
        Text("Welcome")
    }
}

当条件不成立时,SwiftUI 会自动插入一个 EmptyView


4️⃣ switch

@ViewBuilder
func stateView(_ state: LoadState) -> some View {
    switch state {
    case .loading:
        ProgressView()
    case .success:
        Text("Success")
    case .error:
        Text("Error")
    }
}

五、最常见的使用场景

1️⃣ 自定义组件的内容闭包

struct Card<Content: View>: View {
    let content: Content

    init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }

    var body: some View {
        VStack(spacing: 8) {
            content
        }
        .padding()
        .background(.gray.opacity(0.2))
        .cornerRadius(12)
    }
}

使用时:

Card {
    Text("Title")
    Text("Subtitle")
}

👉 这正是 SwiftUI 组件化体验优秀的原因之一。


2️⃣ 模仿系统 API(如 .sheet / .toolbar

func customOverlay<Content: View>(
    @ViewBuilder content: () -> Content
) -> some View {
    overlay {
        content()
    }
}

六、常见坑点(非常容易踩)

❌ 1. 不能写普通逻辑代码

@ViewBuilder
var body: some View {
    let count = 10 // ❌ 编译错误
    Text("(count)")
}

原因是:

@ViewBuilder 只接受 生成 View 的表达式

✅ 正确方式:

var count: Int { 10 }

@ViewBuilder
var body: some View {
    Text("(count)")
}

❌ 2. 不能直接使用 for 循环

@ViewBuilder
var body: some View {
    for i in 0..<3 { // ❌
        Text("(i)")
    }
}

✅ 正确方式:

ForEach(0..<3, id: .self) { i in
    Text("(i)")
}

七、什么时候需要主动使用 @ViewBuilder

当你遇到以下情况时,就该考虑它:

  • 希望一个函数 / 闭包返回 多个 View
  • 需要在返回 View 时使用 if / else / switch
  • 编写 可组合的自定义组件

简单判断法则:

“这个 API 是否应该像 SwiftUI 一样写 UI?”

如果答案是「是」,那基本就需要 @ViewBuilder


八、总结

  • @ViewBuilder 是 SwiftUI 的核心基础设施
  • 它让 Swift 支持 声明式 UI 语法
  • 条件渲染、多 View 组合、本质都依赖它
  • 写组件时,合理使用 @ViewBuilder 能极大提升 API 体验

一句话总结:

没有 @ViewBuilder,就没有今天的 SwiftUI。


如果你觉得这篇文章有帮助,欢迎点赞 / 收藏 / 交流 🙌

后续也可以深入聊:

  • ViewBuilder 源码实现
  • @ViewBuilder 与 @ToolbarContentBuilder 的区别
  • SwiftUI 新数据流(@Observable / @Bindable)下的最佳实践
❌