Search code examples
swiftuiswiftui-navigationstack

Animation toolbarBackground visibility not works


I'm trying to set toolbarBackground visibile with animation, but it's happen immediatly. Are there ways to make this with animation?

struct MyView: View {
    @State private var barHidden = true
    
    var body: some View {
        NavigationStack {
            Button("action") {
                barHidden.toggle()
            }
            .animation(.easeInOut, value: barHidden)
            .toolbar {
                ToolbarItem(placement: .principal) {
                    Text("Title")
                }
            }
            .toolbarBackground(Color.red, for: .navigationBar)
            .toolbarBackground(barHidden ? .hidden : .visible, for: .navigationBar)
        }
    }
}


Solution

  • All you need to do is take advantage of the safeAreaInsets modifier to simulate the animation of the toolbar background. Here's what I've done:

    1. Get the safeArea size using a GeomtryReader
    2. Apply the safeAreaInsets modifier to the NavigationStack to place a View where the toolbar is supposed to be, which, in this case, will be Rectangle
    3. Add an overlay to aforementioned Rectangle that will be our title

    Here's the code:

    struct MyView: View {
        @State private var barHidden = true
        
        var body: some View {
            GeometryReader { proxy in
                let safeArea = proxy.safeAreaInsets
                NavigationStack {
                    Button("action") {
                        withAnimation(.easeInOut) {
                            barHidden.toggle()
                        }
                    }
                }
                .safeAreaInset(edge: .top) {
                    Rectangle()
                        .fill(barHidden ? .clear : .red)
                        .overlay(alignment: .center) {
                            Text("Title")
                                /// Placed exactly where the default Title appears when using the toolbar modifier
                                .padding(.top, safeArea.top / 2)
                        }
                        /// The red area I was seeing with your oringinal code was 2 times the safe area .top
                        .frame(height: safeArea.top * 2)
                        .ignoresSafeArea(.container, edges: .top)
                }
            }
        }
    }
    

    Here's the result:

    Animated Toolbar Backgound