15

I have two Views and each of them contains NavigationView with title. I have created a TabBar View which also has a NavigationView on it.

struct TabbarView: View {
var body: some View {
    NavigationView{
    TabView {
        MainContentView()
            .tabItem {
                VStack {
                    Text("Main")
                }
        }.tag(0)

        SearchContentView()
            .tabItem {
                VStack {
                    Text("Search")
                }
        }.tag(1)
    }
    }.navigationBarBackButtonHidden(true)
    .navigationBarHidden(true)
}

}

I have tried hiding the navigationBar for this view but that doesn't work. Only the navigation bar of this view appears.

This is MainContentView()

struct MainContentView: View {

var body: some View {
    NavigationView {
        Text("Some Content View")
        }
    .navigationBarTitle("Travel")
}

}

Any idea how to go about this. Thanks!

Update: Basically when I tap on a Log In Button, I am passing TabBarView() through NavigationLink.

   NavigationLink(destination: TabbarView()) {
                        HStack {
                            Text("Log In")
                        }
                        .padding()
                        .frame(width: geometry.size.width - 40, height: 40)
                        .foregroundColor(Color.white)
                        .background(Color.blue)
                        .cornerRadius(5)
                }.padding(.bottom, 40)

In doing that, it shows the TabbarView() with child views this is what I see: The space above "Travel" (navigationBarTitle of the childView) is the navigationBar of the tabbar since I am pushing it into navigationStack.

enter image description here

Osama Naeem
  • 1,830
  • 5
  • 16
  • 34

3 Answers3

17

The first thing to point out here is that all of the navigation bar modifiers you have in your code should be modifiers on a view inside of the NavigationView, not modifiers on NavigationView itself. From the documentation for .navigationBarTitle, for example:

This modifier only takes effect when this view is inside of and visible within a NavigationView.

Also, there is no need to have a NavigationView wrapping your TabView and then another inside your MainContentView. This will only lead to nested navigation bars, and you definitely don't want that. Instead, just use one NavigationView. I would also suggest that you not put the NavigationView inside the MainContentView body.

I've refactored your code to show what I'm talking about, although I wasn't sure where you were trying to use .navigationBarBackButtonHidden and .navigationBarHidden, so I omitted them. Just keep in mind that they function just like .navigationBarTitle - you need to use them as modifiers on a view inside NavigationView, not on NavigationView itself.

struct TabBarView: View {
    var body: some View {
        TabView {
            NavigationView {
                MainContentView()
            }
                .tag(0)
                .tabItem {
                    Text("Main")
                }

            SearchContentView()
                .tag(1)
                .tabItem {
                    Text("Search")
                }
        }
    }
}
struct MainContentView: View {
    var body: some View {
        Text("Some Content View")
            .navigationBarTitle("Travel")
    }
}

As you might notice, I also removed the VStack from .tabItem. You can put both Text and Image inside .tabItem without the need for a VStack, and if I'm not mistaken, .tabItem ignores anything that is not Text or Image anyway.

Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113
graycampbell
  • 7,430
  • 3
  • 24
  • 30
  • Thanks a lot for the detail answer and explaining the concept. I will implement it tomorrow. Thanks again! – Osama Naeem Aug 02 '19 at 20:41
  • I implemented the above code and it works fine. However, the child views inside tabView still appears in middle of the screen. As in the 'Travel' title appears few inches below where it actually should be. It's as if the tabbar has a navigation bar as well. – Osama Naeem Aug 03 '19 at 08:50
  • @OsamaNaeem I don't have that same issue. Can you add a picture of the canvas to your question so I can see what you're referring to? – graycampbell Aug 03 '19 at 09:34
  • Hey, I just updated the question. I guess it seems to be a tab bar being pushed into the navigationStack. – Osama Naeem Aug 03 '19 at 11:18
  • In a navigation stack, there should only be one `NavigationView`. Otherwise you’ll end up with nested navigation bars like you have there. You also should not be pushing a tab bar onto the navigation stack - that’s not how tab bars were designed to be used. If you need to push these views onto the navigation stack, then you should remove the tab bar and find another way to layout your view hierarchy. Maybe search could be a bar button item that presents a modal view - just so long as you only have one navigation view per navigation stack and one tab bar switching between different view stacks. – graycampbell Aug 03 '19 at 11:35
  • Thanks for the answer. If anyone wants the navigation bar to encroach beneath the top safe area as normal, you need to add `edgesIgnoringSafeArea(.top)` to the `TabView` itself – tomcully Mar 11 '20 at 19:56
  • if I nest NavigationView inside a TabView, I see the TabView bottom bar on every childview I push on my navigation view. is there any way to hide that ? – ILuvProgramming Jan 25 '21 at 07:57
  • This is not a good answer because you are not able to hide tab view in child views.... purpose is defeated – Andre Feb 01 '21 at 01:03
  • 1
    @Andre Hiding the tab bar in child views doesn’t have anything to do with this question. Also, hiding the tab bar is done using `hidesBottomBarWhenPushed` which is not available in SwiftUI at the moment as far as I know. Nesting a `TabView` inside a `NavigationView` is NOT the way to allow for hiding of the tab bar. – graycampbell Feb 01 '21 at 01:11
  • @graycampbell I'm just now learning that fact. Seems that the documentation for this is not too great. Thanks for clarifying! – Andre Feb 01 '21 at 01:15
2

If you need Login/SignUp View before tabview do not use NavigationView to wrap it. In the Login/Sign up view

@EnvironmentObject var authService:AuthService

var body: some View{
    ZStack{
        if(!authService.isSignedIn){
            Button(action: {
                self.authService.signIn()
            }) {
                Text("Login")
            }
        }
        else{
            TabBarView()
        }
    }
}

In the subviews you can control the isSignedIn variable with @EnvironmentObject and change that value when Signed Out This is an example of the TabBarView() used before:

var body: some View {
    TabView {
        NavigationView{
            FirstView()
        }
        .tabItem {
            VStack{
                Image("first")
                Text("First")
            }
        }
        NavigationView{
            SecondView()
        }
        .tabItem {
            VStack{
                Image("second")
                Text("Second")
            }
        }
    }
}

This is could be one of the tabviews:

@EnvironmentObject var authService:AuthService

var body: some View {
    TextView("Hello World")
    .navigationBarItems(trailing: LogOutButton(logOutFunction: authService.signOut))
}
Daniel Bastidas
  • 1,795
  • 1
  • 9
  • 15
0

This works too in iOS 13:

var body: some View {
        TabView {
            NavigationView {
                FirstTabbarView(viewModel: viewModel)
                    .navigationBarTitle("NavBar title Tabbar1", displayMode: .inline)
            }
            .tabItem {
                Image(systemName: "house.fill")
                Text("Tab bar 1")
            }
            
            NavigationView {
                SecondTabbarView(viewModel: viewModel)
                    .navigationBarTitle("Navbar title Tabbar 2", displayMode: .inline)
            }
            .tabItem {
                Image(systemName: "person.fill")
                Text("Tab bar 2")
            }
            
        }
    }
Naishta
  • 11,885
  • 4
  • 72
  • 54