Tab bar animates from first tab to restored selection at launch with .tabBarMinimizeBehavior(.onScrollDown)

[Submitted as FB23998635]

On iPhone, applying .tabBarMinimizeBehavior(.onScrollDown) to a SwiftUI TabView causes the native tab bar to briefly show the first declared tab before animating to the already-restored selection during app launch.

The selected tab is persisted with @AppStorage and is already restored before the TabView is presented. The sample contains no explicit animations, transactions, navigation containers, loading states, asynchronous work, or post-launch selection changes.

Removing .tabBarMinimizeBehavior(.onScrollDown) eliminates the launch animation entirely. Likewise, starting with .tabBarMinimizeBehavior(.never) and changing it to .onScrollDown after a one-second delay also eliminates the issue.

The behavior reproduces with three simple tabs and a direct @AppStorage selection binding.

ENVIRONMENT

• iOS 26 & 27

REPRO STEPS

  1. Build and run the attached sample.
  2. Select the "Two" tab.
  3. Force-quit the app.
  4. Relaunch the app.
  5. Observe the tab bar during launch.

ACTUAL

The tab indicator initially appears on "One", the first declared tab, then animates to the correctly restored "Two" selection.

EXPECTED

The restored tab should be selected and stationary from the first visible frame, with no launch animation.

SAMPLE CODE

struct ContentView: View {
    private enum AppTab: String, Hashable {
        case one
        case two
        case three
    }

    @AppStorage("selectedGenericTab") private var selectedTab: AppTab = .three

    var body: some View {
        TabView(selection: $selectedTab) {
            Tab("One", systemImage: "1.circle", value: AppTab.one) {
                ReproTabContent(title: "One", color: .orange)
            }

            Tab("Two", systemImage: "2.circle", value: AppTab.two) {
                ReproTabContent(title: "Two", color: .blue)
            }

            Tab("Three", systemImage: "3.circle", value: AppTab.three) {
                ReproTabContent(title: "Three", color: .green)
            }
        }
        .tabBarMinimizeBehavior(.onScrollDown)
    }
}

private struct ReproTabContent: View {
    let title: String
    let color: Color

    var body: some View {
        ZStack {
            color.opacity(0.2)
                .ignoresSafeArea()

            Text(title)
                .font(.largeTitle)
        }
    }
}

A native-only workaround is to start the TabView with minimization off, then switch to .onScrollDown after its initial presentation.

In my testing, this lets the restored selection settle before minimization is applied, preventing the animation from the first tab to the restored tab. The tab bar then minimizes normally after the one-second delay.

The tradeoff is that scroll-driven minimization is unavailable during that first second.

Many of Apple’s first-party iPhone apps restore the previously selected tab at launch while also supporting tab bar minimization, so this shouldn’t require an app-level workaround.

Workaround

struct ContentView: View {
    @AppStorage("selectedGenericTab")
    private var selectedTab: AppTab = .three

    @State private var minimizeBehavior: TabBarMinimizeBehavior = .never

    var body: some View {
        TabView(selection: $selectedTab) {
            // Tabs…
        }
        .tabBarMinimizeBehavior(minimizeBehavior)
        .task {
            guard minimizeBehavior == .never else { return }

            do {
                try await Task.sleep(for: .seconds(1))
            } catch {
                return
            }

            minimizeBehavior = .onScrollDown
        }
    }
}
Tab bar animates from first tab to restored selection at launch with .tabBarMinimizeBehavior(.onScrollDown)
 
 
Q