Issue #147

热茶还是冰咖啡

Cover for Weekly Issue 147

Photo by Alex Meier on Unsplash

最近我所在的城市,气温不断刷新历史纪录。从 7 月末开始,便陷入了持续十天以上、超过 30 摄氏度的高温酷暑。尽管从绝对数值上看并不算惊人,但配上超过 80% 的湿度,即便全天待在 25 度的空调房里,体内依然能攒出一股挥之不去的燥热。

我会通过喝茶、喝咖啡来改善“体感”。有意思的是,唯有热茶能带给我一种热量随汗水释放的通透感,而也唯有冰咖啡能带来片刻直截了当的清凉。我想,这未必是饮品本身的物理特性使然,或许更多是长期饮食习惯在心中留下的心理暗示。

中国有句老话叫“心静自然凉”。我自认还没达到这般超脱的心境,现阶段仍免不了借助这些杯中物,在冷热之间寻得一点平衡。

但愿这个闷热的夏天,能早点复归常态。

近期推荐

Liquid Glass:UIKit 适配踩坑实录

Liquid Glass 在带来全新交互体验的同时,也让 UIKit 的兼容性变得更加复杂。同一套 UI,在 iOS 18、iOS 26 与 iOS 27 上可能表现出截然不同的行为;不同的 API 组合与实现方式,也可能触发各式各样的兼容性问题。Megabits 结合真实项目经验,总结了 UIKit 下 Sheet、Navigation、Popover、Toolbar 等多个典型场景中的适配陷阱,并分析了 UIBarButtonItemCABackdropLayer、Scroll Edge Effect 等底层行为,给出了对应的解决方案与可运行 Demo。


两度绘制的像素渲染地图 (The Map, Drawn Twice)

Apple 的 UI 渲染体系涉及 Core Graphics、Core Text、Core Image、Core Animation 与 SwiftUI 等多个层次,开发者虽然经常使用这些框架,却未必真正理解它们各自承担的职责。在 Apple UI Insider 的创刊号中,Mihaela Mihaljević Jakić 绘制了一张从 Swift 代码到最终像素的职责地图。

Mihaela 采用了一种很有意思的验证方式:每一张插图都由同一份场景描述生成两次,一次交给自己实现的 clean-room 渲染引擎,另一次交给 Apple 的 Core Graphics,再逐像素比较两者的输出。尽管约有一半像素存在差异,但平均像素差异仅约为 0.1%,视觉效果几乎完全一致。这种通过独立实现、可复现实验和量化结果来研究系统行为的方法,比文章本身介绍的内容更值得关注,也让人更加期待这一系列后续的研究。


终于为 .fixedSize 找到了合适场景 (Finally found a use case for .fixedSize)

.fixedSize 是 SwiftUI 中一个经常被忽视的 modifier,不少开发者对它的印象仍停留在避免文本折行等少数场景。⁠Omar Elsayed 从一个很常见的需求出发:如何让横向 ScrollView 中所有卡片都与最高的那一项保持相同高度,分享了自己借助 .fixedSize 找到解决方案的过程。

或许 .fixedSize 一直没有得到广泛使用,与它略显抽象的命名也有一定关系。究竟固定的是什么?对应的 size 又是什么?如果想深入理解它为何能够改变布局结果,可以参阅我之前写的《⁠SwiftUI 布局:尺寸》系列,其中通过实现 fixedSize 的复制品,拆解了它在 SwiftUI 布局协商中的工作机制。


关于 LazyVStack 布局机制的两次有趣探索

在 Apple Developer Forums 上,⁠alextar04 连续发布了两篇关于 LazyVStack 的分析帖。两篇帖子分别围绕快速滚动时可能出现的空白区域与 Cell 截断,以及 GeometryReaderLazyVStack 中获取到的尺寸为何会多次变化展开(⁠帖子 2)。作者不仅提供了最小复现工程,还结合 WWDC 26 关于 Lazy Stack 的介绍、SwiftUI 日志以及实验结果,尝试还原 SwiftUICore 底层(如符号计算与 CA::Transaction 提交验证)在估算尺寸、校验布局时可能采用的工作方式,并据此解释这些现象产生的原因。

这两篇帖子也从另一个角度展现了 ListLazyVStack 在设计理念上的差异。作者推测,LazyVStack 为了实现懒布局,会在部分场景下依赖尺寸估算等机制,因此与 List 呈现出不同的行为。这也说明了为什么两者并不存在绝对的优劣,而是⁠各自适用于不同的场景


Swift on Windows:一年的打磨与完善 (Swift on Windows: A year of refinement)

Swift on Windows 在一年前已经能够支撑真实的生产应用,但“可以运行”并不等于“足够可预测”。⁠The Browser Company 与 Apple 及 Swift 社区将重点放在了基础设施的打磨上:使用现代 CMake 重构 Runtime 构建系统,推出 Experimental Swift SDK,明确静态与动态链接模型,并改善多工具链环境中的 Runtime 选择与分发问题。

真正推动跨平台成熟度提升的,往往不是某个引人注目的新特性,而是这些容易被忽视的工程细节:构建配置是否可审计、Runtime 能否精确绑定、工具是否可以独立分发,以及 CI 与开发环境能否始终保持一致。这篇文章很好地展示了 Swift 在 Windows 平台过去一年最重要的进步,更多来自工程能力的持续打磨,而非功能上的扩张。


MemoProperty:在 SwiftUI 中复用有状态业务逻辑 (Memo Property: Reusing More Stateful Business Logic for SwiftUI Views)

TaskProperty 中,Rick Van Voorden 展示了如何借助自定义 DynamicProperty,将带有生命周期和状态的业务逻辑从 View 中抽离出来。本文沿着相同的思路继续向前,结合 Swift 最新的参数包(Parameter Packs / Variadic Generics)等前沿语法,尝试在 SwiftUI 中实现一个类似 React useMemo 的机制:只有当显式声明的依赖发生变化时,才重新执行过滤、排序等计算,否则直接复用此前缓存的结果,避免 View 因无关状态变化而重复完成昂贵工作。


多流编辑下的状态同步:构建模型与 UI 之间缺失的“投影层” (One Model Object, Multiple Editing Flows: The Missing Layer Between Model and UI)

Observation 出现之后,不少开发者开始认为 Combine 在 SwiftUI 中已经逐渐失去价值。Sergey Tristan 的这篇文章则提供了一个不同的视角:当同一个模型对象需要被多个独立的编辑流程共享,并且每个流程只关心其中一部分状态时,Observation 负责高效地感知变化,而 Combine 更适合承担数据组合、转换与持续同步的职责,两者并非替代关系,而是可以各司其职。

文章提出了一个位于 Model 与 UI 之间的 Projection 概念,将一个或多个持续变化的数据源转换为不同消费者所需的响应式表示。无论是否认同这种架构,它都提出了一个值得思考的问题:当应用开始拥有越来越多独立却又相互关联的编辑流程时,是否需要一种不同于 DTO、ViewModel 或 Observation 的抽象来组织这些持续演化的数据流?

工具

Apple 外接显示器支持查询

苹果设备对外接显示器的支持一直比较复杂,不同芯片、不同机型,甚至不同系统版本之间都存在明显差异。由 Parish Khan 创建的 RetinaDesk 提供了一个在线查询工具,可以快速了解 Mac、iPad、iPhone 等设备支持的外接显示器数量、最高分辨率、刷新率,以及是否支持扩展显示、镜像模式等信息。

对于需要测试多屏显示、Stage Manager 或外接显示器相关功能的开发者,这也是一个很方便的参考工具,能够快速确认不同设备组合的能力边界,而无需逐一查阅 Apple 官方技术规格。


Agent Island

Agent Island 是一个由 ⁠Tristan Tang 开发的开源本地状态监视工具,它会持续读取 Claude Code 与 Codex 在本地生成的会话记录,实时判断当前状态,并通过菜单栏中的“灵动岛”样式组件提供直观的状态显示与提醒,让开发者无需一直盯着终端。它还支持在 AI 模型额度窗口重置后自动执行预设的后续操作,帮助长时间运行的任务更顺畅地衔接下去。

项目采用 Local-first 设计,不依赖云端服务,不上传任何会话内容,同时提供 Token 使用统计、额度重置倒计时、周/月报告等功能。

相关周报

订阅 Fatbobman 周报

每周精选 Swift 与 SwiftUI 开发技巧,加入众多开发者的行列。

立即订阅