Leon . Kang
Back to articles
📱 Coding 2/3/2026 10 min read

SwiftUI navigationTransition 在真实项目中的问题与版本影响分析

背景:EXIF Master 中的 Hero Transition 需求

在开发 EXIF Master 的过程中,我希望在照片列表页到照片详情页之间加入类似 Hero Transition 的过渡效果。
SwiftUI 在近几个版本中提供了:

  • .navigationTransition(.zoom)
  • .matchedTransitionSource

从 API 设计上看,这是官方给出的标准解法。

目标行为很明确:

  1. 列表页显示照片缩略图
  2. 点击照片,使用 zoom 动画 push 到详情页
  3. 使用系统侧滑手势返回
  4. 返回后,原照片应仍然可见

但实际行为并不符合预期。


问题现象:手势返回后源照片消失

在真实设备和模拟器中复现的现象如下:

  • 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 恢复失败有关

技术原因(推测)

结合社区分析,可以合理推断:

  1. iOS 26 中 NavigationStack 内部实现发生了变更
  2. matchedTransitionSource 在动画结束后需要“回填”源视图状态
  3. 当动画被手势中断或提前结束时:
    • 源视图未被正确重新注册到渲染树
    • 结果是视图存在,但不参与绘制

这不是 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 完成,开发流程都被显著加速。开发者因此能够把时间从无意义的重复调试中释放出来,转而思考真正重要的问题: 产品功能是否合理、交互是否符合真实的人类使用习惯,以及系统在真实环境中的表现。