Search code examples
swiftuiswiftui-navigationlink

How to stop pushed view from temporarily rendering hidden navigationBar?


Unexpectedly, SwiftUI's pushed view temporarily renders its contents with space for the hidden navigationBar upon transition. A moment later it re-renders properly. How do I prevent this behavior?

For GIF screen recording, click the image below.

jumpy render

ContentView.swift

import SwiftUI

struct ContentView: View {
    @State var goToNextView = false

    var body: some View {
        NavigationView { ZStack {
            /*@START_MENU_TOKEN@*/Color.yellow/*@END_MENU_TOKEN@*/.edgesIgnoringSafeArea(.all)
            NavigationLink(destination: SecondView(), isActive: $goToNextView) {Text("")}
                .navigationBarTitle("")
                .navigationBarHidden(true)
                .navigationBarBackButtonHidden(true)


            VStack {

                Button(action: {
                    print("Button clicked")
                    self.goToNextView = true
                }) { Text("Go to second view") }
                    .padding()
                Text("This is the first view.")

            }
        }
        .foregroundColor(Color.blue)

        }
    }
}

SecondView.swift

struct SecondView: View {

    var body: some View {

        ZStack {

            Color.purple
            .edgesIgnoringSafeArea(.all)

            .navigationBarBackButtonHidden(true)
            .navigationBarHidden(true)

            VStack { Text("Pushed view") }

        }
        .foregroundColor(Color.white)

    }
}

Solution

  • I removed this behavior by utilizing a View Modifier, influenced by this answer.

    Inline comments explain the changes I made.

    import SwiftUI
    
        struct ContentView: View {
            @State var goToNextView = false
    
            var body: some View {
                NavigationView { ZStack {
                    /*@START_MENU_TOKEN@*/Color.yellow/*@END_MENU_TOKEN@*/.edgesIgnoringSafeArea(.all)
                    NavigationLink(destination: SecondView(), isActive: $goToNextView) {Text("")}
                        // Removed all nav config code here
                    VStack {
                        Button(action: {
                            print("Button clicked")
                            self.goToNextView = true
                        }) { Text("Go to second view") }
                            .padding()
                        Text("This is the first view.")
                    }
                }
                // Added this to hide bar
                .hiddenNavigationBarStyle()
                .foregroundColor(Color.blue)
    
                }
            }
        }
    
        struct SecondView: View {
            var body: some View {
                ZStack {
                    Color.purple
                    .edgesIgnoringSafeArea(.all)
                        // Added this to hide bar
                        .hiddenNavigationBarStyle()
                    VStack { Text("Pushed view") }
                }
                .foregroundColor(Color.white)
            }
        }
    

    This is the View Modifier taken from the earlier answer:

    struct HiddenNavigationBar: ViewModifier {
        func body(content: Content) -> some View {
            content
            .navigationBarTitle("", displayMode: .inline)
            .navigationBarHidden(true)
        }
    }
    
    extension View {
        func hiddenNavigationBarStyle() -> some View {
            ModifiedContent(content: self, modifier: HiddenNavigationBar())
        }
    }