iosswiftuitoolbarswiftui-navigationstackios26

ToolbarItem flickers when switching between tabs in iOS 26


How can I avoid the flickering ToolbarItem when switching between tabs, while still keeping a dedicated NavigationStack for each Tab?

enter image description here

I have unsuccessfully tried:

struct MyTabView: View {
    var body: some View {
        TabView {
            Tab {
                MyNavStack()
            } label: {
                Image(systemName: "house")
            }
            
            Tab {
                MyNavStack()
            } label: {
                Image(systemName: "star")
            }
        }
    }
}
struct MyNavStack: View {
    var body: some View {
        NavigationStack {
            List {
                NavigationLink("Mint", value: Color.mint)
                NavigationLink("Pink", value: Color.pink)
            }
            .navigationDestination(for: Color.self) { color in
                Text("You selected \(color.description)")
            }
            .toolbar {
                ToolbarItem(placement: .topBarLeading) {
                    Button("Flickers") { }
                }
            }
        }
    }
}

Solution

  • It seems that the flickering is coming from the "shared background" around the toolbar button.

    The flickering can be prevented by hiding the shared background:

    ToolbarItem(placement: .topBarLeading) {
        Button("Flickers") { }
    }
    .sharedBackgroundVisibility(.hidden) // ๐Ÿ‘ˆ here
    

    However, when this is done, the button no longer looks like a glass button.

    You can get it back to a similar appearance by applying a glass button style. It is also necessary to apply .fixedSize, otherwise the button is round and the label is truncated:

    ToolbarItem(placement: .topBarLeading) {
        Button("Flickers") { }
            .fixedSize() // ๐Ÿ‘ˆ here
            .buttonStyle(.glass) // ๐Ÿ‘ˆ and here
    }
    .sharedBackgroundVisibility(.hidden)
    

    This is close to the appearance that it had without the modifiers, but the button is just a bit smaller.

    For better control of the appearance, you can use a custom ButtonStyle:

    struct GlassCapsuleButton: ButtonStyle {
        func makeBody(configuration: Configuration) -> some View {
            configuration.label
                .fixedSize()
                .padding(11)
                .contentShape(.capsule)
                .glassEffect(.clear.interactive(), in: .capsule)
        }
    }
    

    As a convenience, a ButtonStyle extension can also be defined:

    extension ButtonStyle where Self == GlassCapsuleButton {
        static var glassCapsule: Self { Self() }
    }
    

    Applying the custom button style:

    ToolbarItem(placement: .topBarLeading) {
        Button("Flickers") { }
            .buttonStyle(.glassCapsule) // ๐Ÿ‘ˆ here
    }
    .sharedBackgroundVisibility(.hidden)
    

    Animation