Search code examples
iosswiftuiswiftui-navigationlinkswiftui-navigationviewswiftui-tabview

How to hide TabView when opening a new view with NavigationLink?


Assuming we have a SwiftUI view containing

struct ContentView: View {
    var body: some View {
        TabView {
            FirstView().tabItem {
                // tabItem image and text
            }
            SecondView().tabItem {
                // tabItem image and text
            }
        }
    }
}

Now, let's say FirstView contains a NavigationView with scrollable content using NavigationLink for each element. How can I make it such that when a NavigationLink destination is triggered (i.e. a child view is opened), that it takes over the whole page (in full-screen) and thus hides the TabView?

Ideally I would like to support iOS 13+.

I have tried to follow the guidance at Hacking with Swift but to no avail.

I also followed the advice in SwiftUI Hide TabView bar inside NavigationLink views but found that the top solution is not so performant, so I am hoping to achieve a solution without a delayed appearance.


Solution

  • Enclose the contents of your tabitem inside an if condition that checks a shared state:

    struct ContentView: View {
    
        @StateObject private var state = State()
    
        var body: some View {
            TabView {
                FirstView().tabItem {
                    if !state.hideTabView {
                        // tabItem image and text
                    }
                }
                SecondView().tabItem {
                    if !state.hideTabView {
                        // tabItem image and text
                    }
                }
            }
            .environmentObject(state)
        }
    }
    

    Where State is an ObservableObject as such:

    class State: ObservableObject {
        @Published hideTabView: Bool = false
    }
    

    Then you can use onAppear on the View that is linked to via NavigationLink (e.g inside FirstView):

    struct FirstView: View {
        
        @EnvironmentObject private var state: State
        var body: some View {
            VStack {
                // ... some content
            }
            .onAppear(perform: { state.hideTabView = true })
            .onDisappear(perform: { state.hideTabView = false })
        }
    }
    

    There is a slight delay on the TabView appearing again when you press "< Back", if that really bothers you then you can make a custom Back button and move this state.hideTabView = false to the event of tapping that button.

    That is one approach I can think of :) Also, you might find this thread helpful.