Skip to content

Navigation push animation breaks after presenting a sheet/fullScreenCover on iOS 16 #3833

@kalupas226

Description

@kalupas226

Description

This is a separated issue for #3830.

On iOS 16, the first navigation push after presenting a sheet (or fullScreenCover) loses its animation. The issue recurs each time a modal is presented. Every new modal cycle causes the next initial push to exhibit the broken animation. This does not reproduce in vanilla SwiftUI. This issue reproduces on iOS 16, but does not occur on iOS 17 or later.

I looked into this issue and found a potential cause (click to expand)

It seems like there might be a timing difference between Vanilla SwiftUI and TCA when initializing NavigationStack inside a sheet.

Vanilla SwiftUI:

  • @State var path = NavigationPath() is a value type fully managed by SwiftUI
  • This might be perfectly synchronized with SwiftUI's internal timing

TCA:

  • The Store is a reference type injected from outside via the sheet closure
  • On iOS 16, SwiftUI's internal NavigationStack state management might not be fully prepared during the sheet presentation animation

I have the following hypothesis:

  • Sheet is presented
  • _NavigationDestinationViewModifier is created with @SwiftUI.State var store holding a reference to the Store
  • During the sheet presentation animation, SwiftUI's internal NavigationStack state might not be fully ready
  • On the first push, navigationDestination(for:) closure is called, but the animation context could be missing
  • Subsequent pushes work correctly because SwiftUI's internal state is now properly initialized

I tried the following workaround on a small repro project, which delays the NavigationStack initialization slightly, and it seemed to resolve the issue:

struct TCASheetView: View {
  @Perception.Bindable var store: StoreOf<TCASheet>
  @State private var isReady = false

  var body: some View {
    WithPerceptionTracking {
      if isReady {
        NavigationStack(
          path: $store.scope(
            state: \.path,
            action: \.path
          )
        ) {
          VStack {
            Button("Push") {
              store.send(.pushButtonTapped)
            }
          }
          .navigationTitle("Sheet")
        } destination: { store in
          WithPerceptionTracking {
            switch store.case {
            case let .sheetChild(store):
              TCASheetChildView(store: store)
            }
          }
        }
      } else {
        Color.clear
          .onAppear {
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
              isReady = true
            }
          }
      }
    }
  }
}

This might suggest that the timing of NavigationStack initialization during sheet presentation is related to the problem.

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

Push navigation should animate normally after presenting a sheet/fullScreenCover. I attached a video from vanilla SwiftUI showing the animation working as expected.

Simulator.Screen.Recording.-.iPhone.8.-.2026-01-01.at.15.30.48.mov

Actual behavior

In the TCA version, the first push after presenting a sheet/fullScreenCover has a missing animation on iOS 16, and the issue repeats on each new modal presentation. I attached a video from the TCA setup showing the missing animation.

Simulator.Screen.Recording.-.iPhone.8.-.2026-01-01.at.15.30.03.mov

Reproducing project

I attached a small repro project that lets you compare the TCA version and the vanilla SwiftUI version. You can switch between them in PushNavigationApp.swift.

PushNavigation.zip

The Composable Architecture version information

1b3940c (main branch)

Destination operating system

iOS 16.4

Xcode version information

Version 26.2 (17C52)

Swift Compiler version information

swift-driver version: 1.127.14.1 Apple Swift version 6.2.3 (swiftlang-6.2.3.3.21 clang-1700.6.3.2)
Target: arm64-apple-macosx26.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working due to a bug in the library.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions