Leon . Kang
Back to articles
📱 Coding 2/3/2026 10分で読める

実プロジェクトにおけるSwiftUI navigationTransitionの問題とバージョン影響分析

背景:EXIF MasterにおけるHero Transition要件

EXIF Masterの開発過程で、写真リストページと写真詳細ページの間でHero Transitionのような遷移効果を追加したいと考えました。 SwiftUIは最近のいくつかのバージョンで以下を提供しています:

  • .navigationTransition(.zoom)
  • .matchedTransitionSource

API設計から判断すると、これが公式の標準的な解決策です。

目標とする動作は明確です:

  1. リストページに写真のサムネイルを表示する。
  2. 写真をタップすると、ズームアニメーションで詳細ページにプッシュ遷移する。
  3. システムのサイドスワイプジェスチャーを使用して戻る。
  4. 戻った後、元のリストの写真は表示されたままであること。

しかし、実際の動作は期待通りではありませんでした。


現象:ジェスチャーで戻ると元の写真が消える

実機およびシミュレーターで再現された現象は以下の通りです:

  • 写真詳細ページへのプッシュ時のアニメーションは正常。
  • 戻るボタンで戻る場合、ほとんどの場合は正常。
  • サイドスワイプジェスチャー(インタラクティブポップ)で戻った後
    • 元のリストにあったその写真が直接消失する
    • データ状態の喪失ではなく、ビューが不可視になる。
  • navigationTransition / matchedTransitionSourceに関連するすべてのコードを削除すると、問題は即座に解消する。

これは次のことを意味します:

問題はデータ、状態、Diffable更新とは無関係であり、アニメーションメカニズムに強く関連している。


調査プロセス:なぜこれが「自分のミス」ではないと言えるのか

これがフレームワークの問題であることを確認する前に、以下の調査を行いました:

  • データソースが変更されていないことを確認(配列、ID、Bindingは安定)。
  • .id()を使用してビューの強制更新を試行。
  • ImageをColor / Rectangleに置き換え。
  • 遅延ロード(LazyVGrid / LazyVStack)を削除。
  • アニメーション、トランザクション、withAnimationを無効化。

結論は非常に一貫していました:

navigationTransitionを削除すると、問題は消失する

このような挙動は、「フレームワークレベルのレンダリングまたはライフサイクルのバグ」の特徴と強く一致しています。


最終確認:これはSwiftUIの公式バグである

完全な再現条件をGPTに説明した後、得られた結論は以下の通りです:

  • これはSwiftUI NavigationStack + navigationTransitionの既知の問題である。
  • 個別のケースではない。
  • 開発者コミュニティで繰り返し報告されている。
  • 現在まで、リリースノートに明確な修正の記載は見られない。

影響を受けるシステムバージョンの範囲

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シリーズでのみ発生
  • インタラクティブポップ(スワイプバック)と強く関連
  • matchedTransitionSourceの復元失敗に関連

技術的な原因(推測)

コミュニティの分析を組み合わせると、以下のように合理的に推測できます:

  1. iOS 26でNavigationStackの内部実装に変更があった。
  2. matchedTransitionSourceはアニメーション終了後にソースビューの状態を「埋め戻す」必要がある。
  3. ジェスチャーによってアニメーションが中断または早期終了した場合:
    • ソースビューがレンダリングツリーに正しく再登録されない。
    • 結果として、ビューは存在するが描画には参加しない。

これはAPIの誤用ではなく、ライフサイクル復元パスの欠落です。


実行可能な回避策(不完全)

案1:プッシュの代わりに fullScreenCover を使用する

詳細ページを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のバグを回避できる。
  • アニメーションは引き続き使用可能。

デメリット:

  • システムレベルのプッシュ/ポップの意味合い(セマンティクス)が失われる。
  • 閉じるロジックを自分で処理する必要がある。
  • UXがネイティブナビゲーションと完全には一致しない。

なぜこの問題は「厄介」なのか

このバグが開発者の時間を大量に消費する理由は以下の通りです:

  • 挙動が「状態管理のエラー」のように見える。
  • ログ、ブレークポイント、デバッグツールが問題を直接指し示さない。
  • サンプルコードや単純なデモでは再現しないことが多い。
  • 実際のインタラクション(ジェスチャーバック)の下でのみ安定して出現する。

これが、私がアニメーションを削除する前に長い時間をかけて調査した理由です。


コーディングとトラブルシューティングにおけるAIの価値

プログラミングやトラブルシューティングにおけるAIの価値は非常に明確になってきました。 以前は、このような問題には以下が必要でした:

  • 大量のフォーラムやIssueを閲覧する。
  • 異なるシステムバージョンを手動で比較する。
  • 消去法で結論に徐々に近づく。

しかし現在、AIは情報が高度に分散している状況でも、「これはあなたの問題ではなく、フレームワークの問題である」と迅速に識別し、バージョンの範囲、原因の推測、実行可能な代替案を提示できます。この効率はかつては想像できないものでした。

コラボレーションツールとしてであれ、境界を明確にした後に実装の一部を直接AIに任せるのであれ、開発プロセスは著しく加速されています。開発者は無意味な繰り返しのデバッグから時間を解放され、真に重要な問題を考えることに時間を割くことができます: 製品の機能は合理的か?インタラクションは人間の実際の使用習慣に合っているか?そして、システムは実際の環境でどのように動作するか?