How to replace tabBar with bottomBar with smooth animation in SwiftUI?

I am trying to replace the navigation tab bar with a custom bottom toolbar when a view enters edit mode. Currently, I am using the following code to achieve this:

content    
    .toolbar(isEditing ? .hidden : .visible, for: .tabBar)
.    toolbar(isEditing ? .visible : .hidden, for: .bottomBar)

However, this results in a janky animation. When the bottom bar appears, it animates in above (in contrast to in place of) the tab bar, then "jumps" back down to the correct offset without animation. I had to workaround this by delaying the appearance of bottom bar by 0.3s. I am already using withAnimation().

Is this a bug or am I using the APIs incorrectly? Is there a more seamless way to achieve this switching effect other than delaying the bottom bar? Thanks!

Answered by DTS Engineer in 794633022

@NSCruiser Here's an example of how to go about it:

struct ContentView: View {
    @State private var isEditing = false

    var body: some View {
        NavigationStack {
            TabView {
                Tab("Received", systemImage: "tray.and.arrow.down.fill") {
                    Button("toogle") {
                        isEditing.toggle()
                    }
                    .toolbar(isEditing ? .hidden : .visible, for: .tabBar)
                }
                .badge(2)
                
                Tab("Sent", systemImage: "tray.and.arrow.up.fill") {
                    Color.blue
                }
                
                Tab("Account", systemImage: "person.crop.circle.fill") {
                    Color.green
                }
                .badge("!")
            }
            .toolbar(isEditing ? .visible : .hidden, for: .bottomBar)
            .animation(.easeIn, value: isEditing)
            .toolbar {
                ToolbarItem(placement: .bottomBar) {
                    Text("hello world")
                }
            }
        }
    }
}

@NSCruiser Here's an example of how to go about it:

struct ContentView: View {
    @State private var isEditing = false

    var body: some View {
        NavigationStack {
            TabView {
                Tab("Received", systemImage: "tray.and.arrow.down.fill") {
                    Button("toogle") {
                        isEditing.toggle()
                    }
                    .toolbar(isEditing ? .hidden : .visible, for: .tabBar)
                }
                .badge(2)
                
                Tab("Sent", systemImage: "tray.and.arrow.up.fill") {
                    Color.blue
                }
                
                Tab("Account", systemImage: "person.crop.circle.fill") {
                    Color.green
                }
                .badge("!")
            }
            .toolbar(isEditing ? .visible : .hidden, for: .bottomBar)
            .animation(.easeIn, value: isEditing)
            .toolbar {
                ToolbarItem(placement: .bottomBar) {
                    Text("hello world")
                }
            }
        }
    }
}

@DTS Engineer Thank you for the example. In my app, TabView is the root view, and NavigationStack is nested within each Tab. I changed the provided example to match the structure of my app, and found that the animation issue was still present. Is this expected behavior?

struct ContentView: View {
    @State private var isEditing = false

    var body: some View {
        TabView {
            Tab("Received", systemImage: "tray.and.arrow.down.fill") {
                NavigationStack {
                    Button("toogle") {
                        isEditing.toggle()
                    }
                    .toolbar(isEditing ? .hidden : .visible, for: .tabBar)
                    .toolbar(isEditing ? .visible : .hidden, for: .bottomBar)
                    .animation(.easeIn, value: isEditing)
                    .toolbar {
                        ToolbarItem(placement: .bottomBar) {
                            Text("hello world")
                        }
                    }
                }
            }
            .badge(2)
            
            Tab("Sent", systemImage: "tray.and.arrow.up.fill") {
                Color.blue
            }
            
            Tab("Account", systemImage: "person.crop.circle.fill") {
                Color.green
            }
            .badge("!")
        }
    }
}

I have submitted FB14464408.

After going down a 5am rabbit hole it’s a shame this still isn’t working

Is there any updates on this? Did you guys find a solution? I'm also neck deep in a rabbit hole currently

It seems like it has something to do with safe area insets in the interim/temporary state in-between the tabbar being fully hidden - and the bottom bar being fully visible. In the interim state the safe area insets are gone, but back again as one of them have appeared fully.

How to replace tabBar with bottomBar with smooth animation in SwiftUI?
 
 
Q