背景:EXIF Master 中的 Hero Transition 需求
在开发 EXIF Master 的过程中,我希望在照片列表页到照片详情页之间加入类似 Hero Transition 的过渡效果。
SwiftUI 在近几个版本中提供了:
.navigationTransition(.zoom).matchedTransitionSource
从 API 设计上看,这是官方给出的标准解法。
目标行为很明确:
- 列表页显示照片缩略图
- 点击照片,使用 zoom 动画 push 到详情页
- 使用系统侧滑手势返回
- 返回后,原照片应仍然可见
但实际行为并不符合预期。
问题现象:手势返回后源照片消失
在真实设备和模拟器中复现的现象如下:
- push 到照片详情页时动画正常
- 点击返回按钮返回时,大多数情况下正常
- 使用侧滑手势(interactive pop)返回后:
- 原列表中的那张照片 直接消失
- 不是状态丢失,而是视图变为不可见
- 删除所有
navigationTransition / matchedTransitionSource相关代码后,问题立即消失
这意味着:
问题与数据、状态、Diffable 更新无关,而与动画机制强相关。
排查过程:为什么这不像是“我的问题”
在确认这是框架问题之前,我做过以下排查:
- 确认数据源未变化(数组、ID、Binding 均稳定)
- 尝试强制
.id()刷新视图 - 将 Image 替换为 Color / Rectangle
- 移除懒加载(LazyVGrid / LazyVStack)
- 禁用动画、事务、
withAnimation
结论非常一致:
只要移除 navigationTransition,问题就消失
这类行为高度符合「框架级渲染或生命周期 Bug」的特征。
最终确认:这是 SwiftUI 的官方 Bug
在向 GPT 描述完整复现条件后,得到的结论是:
- 这是 SwiftUI NavigationStack + navigationTransition 的已知问题
- 并非个别 case
- 在开发者社区中已被反复报告
- 至今未在 release note 中看到明确修复声明
受影响的系统版本范围
根据 Reddit、Stack Overflow、Apple Developer Forum 的反馈汇总,可得出如下结论:
| iOS 版本 | 是否受影响 | 说明 |
|---|---|---|
| iOS 18.x | ❌ 否 | 行为正常 |
| iOS 26.0 (Beta / Release) | ✅ 是 | 最早被大量报告 |
| iOS 26.1 Beta / RC | ✅ 是 | 未修复 |
| iOS 26.1 正式版 | ✅ 是 | 社区持续反馈 |
| iOS 26.2 及之后 | ⚠️ 高概率仍存在 | 尚无明确修复确认 |
核心特征是:
- 只在 iOS 26 系列出现
- 与 interactive pop(侧滑返回)强相关
- 与 matchedTransitionSource 恢复失败有关
技术原因(推测)
结合社区分析,可以合理推断:
- iOS 26 中
NavigationStack内部实现发生了变更 matchedTransitionSource在动画结束后需要“回填”源视图状态- 当动画被手势中断或提前结束时:
- 源视图未被正确重新注册到渲染树
- 结果是视图存在,但不参与绘制
这不是 API 使用错误,而是生命周期恢复路径缺失。
可行的 Workaround(非完美)
方案一:使用 fullScreenCover 替代 push
将详情页从 NavigationStack 中移出:
@State private var showDetail = false
@Namespace private var ns
var body: some View {
Image(...)
.matchedTransitionSource(id: photo.id, in: ns)
.onTapGesture {
showDetail = true
}
.fullScreenCover(isPresented: $showDetail) {
DetailView(photo: photo)
.navigationTransition(
.zoom(sourceID: photo.id, in: ns)
)
}
}
优点
- 避开 NavigationStack 的 bug
- 动画仍可使用
代价
- 失去系统级 push / pop 语义
- 需要自行处理关闭逻辑
- UX 与原生导航不完全一致
为什么这个问题很“隐蔽”
这个 bug 特别消耗开发者时间,原因在于:
- 行为看起来像是「状态管理错误」
- 日志、断点、调试工具无法直接指向问题
- 示例代码往往在简单 Demo 中不复现
- 只有在真实交互(手势返回)下才稳定出现
这也是为什么我在删除动画之前,排查了很久。
AI 在编程与问题排查中的价值
AI 在编程和问题排查中的价值已经非常明确。 过去,这类问题往往需要:
- 翻阅大量论坛和 Issue
- 手动对比不同系统版本
- 通过排除法逐步逼近结论
而现在,AI 可以在信息高度分散的情况下,快速识别「这不是你的问题,而是框架问题」,并给出版本范围、成因推断和可行替代方案。这种效率在过去是难以想象的。
无论是作为协作工具,还是在明确边界后将部分实现直接交由 AI 完成,开发流程都被显著加速。开发者因此能够把时间从无意义的重复调试中释放出来,转而思考真正重要的问题: 产品功能是否合理、交互是否符合真实的人类使用习惯,以及系统在真实环境中的表现。