GithubHelp home page GithubHelp logo

treattrick / hide-tabbar-in-swiftui Goto Github PK

View Code? Open in Web Editor NEW
178.0 5.0 14.0 4.43 MB

This tutorial provides a solution to hide TabBars when using TabView in SwiftUI

Swift 100.00%
swiftui tabview tabbar hidetabbar hide navigationview swift

hide-tabbar-in-swiftui's Introduction

How to Hide TabBar in NavigationView When Using SwiftUI

Recently, more and more people are using SwiftUI to develop iOS apps, but as a new tool SwiftUI still has a lot of unresolved problems.

Lots of developers find they cannot hide TabBar when they use NavigationView to navigate to a new view in SwiftUI. It is pretty annoying.

Here, I would like to give you guys a solution to solve this problem. I will explain some View Hierarchy knowledge first to help you guys understand what's actually going on in swiftUI when we try to use NavigationView and TabView. You can also directly jump to solution if you want to.

The Hierarchy of SwiftUI Views

Here is a demo app. In this demo, I have two tabs, tab1 and tab2, in a TabView, and I want to tap the text in each tab to navigate to NavigatedView. So, I add NavigationView and NavigationLink to the contents in each tab. The code is as below.

ContentView.swift

import SwiftUI

struct ContentView: View {
    @State var tabSelection: Tabs = .tab1
    var body: some View {
        TabView(selection: $tabSelection){
            NavigationView{ //if you write the NavigationView here, you cannot remove TabBar after navigation
                NavigationLink(destination: NavigatedView()){
                    VStack{
                        Text("Here is Tab 1")
                        Text("Tap me to NavigatedView")
                    }
                    .navigationBarTitle("Tab1")
                }
            }
            .tabItem { Text("Tab 1") }
            .tag(Tabs.tab1)
            
            NavigationView{//Tab2 also has a NavigationView
                NavigationLink(destination: NavigatedView()){
                    VStack{
                        Text("Here is Tab 2")
                        Text("Tap me to NavigatedView")
                    }
                    .navigationBarTitle("Tab2")
                }
            }
            .tabItem { Text("Tab 2") }
            .tag(Tabs.tab2)
        }
    }
    
    enum Tabs{
        case tab1, tab2
    }
}

NavigatedView.swift

import SwiftUI

struct NavigatedView: View {
    var body: some View {
        Text("Hi! This is the NavigatedView")
            .navigationBarTitle("NavigatedView")
    }
}

When running the code, there is a problem, you can see when I tap the text in Tab1 and go to the NavigatedView, TabBar is still at the bottom. However, when we develop an app, sometimes we really want the TabBar to disappear when navigating.

Navigation in TabView before navigation after navigation
Navigation in TabView Navigation in TabView Navigation in TabView

Why does it run like this? To understand this, let's take a look at View Hierarchy in SwistUI. When you write the NavigationView in TabView, the things goes in SwiftUI like the following pictures.TabView contains NavigationView, and it makes everything happening in NavigationView cannot affect TabView, because NavigationView is just a child-view of TabView. So, when navigating to another view, NavigationView changes, but as the super-view TabView will stay what it is.

when the Tap Here to a new view button is tapped, only the red part (NavigationView) changes to orange part (NavigatedView), but the blue part ( TabView) stays the same.
Navigation in TabView

Here, it should be clear. If we want to hide the TabBar, we just write TabView into NavigationView, making the NavigationView the super-view and the TabView the child-view, which is just opposite to the above View Hierarchy.

when the Tap Here to a new view button is tapped, the blue part (NavigationView) changes to orange part (NavigatedView), so the TabBar in red part disappears itself.
Navigation in TabView

After knowing this, we just need to modify our code a little to let NavigationView contain TabViewand then we can perfectly solve the problem.

The Solution

Like what has been mentioned above, we just rewrite our code to make NavigationView contain TabView.

import SwiftUI

struct ContentView: View {
    @State var tabSelection: Tabs = .tab1
    var body: some View {
        NavigationView{
            TabView(selection: $tabSelection){
//                NavigationView{ //if you write the NavigationView here, you cannot remove TabBar after navigation
                    NavigationLink(destination: NavigatedView()){
                        VStack{
                            Text("Here is Tab 1")
                            Text("Tap me to NavigatedView")
                        }
                        .navigationBarTitle("Tab1")//NavigationBarTitle does not work here
                    }
//                }
                    .tabItem { Text("Tab 1") }
                .tag(Tabs.tab1)
                
//                NavigationView{//Tab2 also has a NavigationView
                    NavigationLink(destination: NavigatedView()){
                        VStack{
                            Text("Here is Tab 2")
                            Text("Tap me to NavigatedView")
                        }
                        .navigationBarTitle("Tab2")//NavigationBarTitle does not work here
                    }
//                }
                    .tabItem { Text("Tab 2") }
                .tag(Tabs.tab2)
            }
        }
    }
    
    enum Tabs{
        case tab1, tab2
    }
}

Another problem here is our NavigationBarTitle does not display itself when we write it in TabView.

Navigation in TabView before navigation after navigation
Navigation in TabView Navigation in TabView Navigation in TabView

The solution is also easy. Just move the NavigationBarTitle to the end of TabView and it works fine. But in this way, when we tap different tabs, the NavigationBarTitle is always the same, which is not what we want. So, I also added a function returnNaviBarTitle to display the right NavigationBarTitle based on the selected tab. The following code is the final version.

import SwiftUI

struct ContentView: View {
    @State var tabSelection: Tabs = .tab1
    var body: some View {
        NavigationView{
            TabView(selection: $tabSelection){
//                NavigationView{ //if you write the NavigationView here, you cannot remove TabBar after navigation
                    NavigationLink(destination: NavigatedView()){
                        VStack{
                            Text("Here is Tab 1")
                            Text("Tap me to NavigatedView")
                        }
//                        .navigationBarTitle("Tab1")//NavigationBarTitle does not work here
                    }
//                }
                    .tabItem { Text("Tab 1") }
                .tag(Tabs.tab1)
                
//                NavigationView{//Tab2 also has a NavigationView
                    NavigationLink(destination: NavigatedView()){
                        VStack{
                            Text("Here is Tab 2")
                            Text("Tap me to NavigatedView")
                        }
//                        .navigationBarTitle("Tab2")//NavigationBarTitle does not work here
                    }
//                }
                    .tabItem { Text("Tab 2") }
                .tag(Tabs.tab2)
            }
            .navigationBarTitle(returnNaviBarTitle(tabSelection: self.tabSelection))//add the NavigationBarTitle here.
        }
    }
    
    enum Tabs{
        case tab1, tab2
    }
    
    func returnNaviBarTitle(tabSelection: Tabs) -> String{//this function will return the correct NavigationBarTitle when different tab is selected.
        switch tabSelection{
            case .tab1: return "Tab1"
            case .tab2: return "Tab2"
        }
    }
}
Navigation in TabView before navigation after navigation
Navigation in TabView Navigation in TabView Navigation in TabView

End

Here ends the doc! Hope you have learned something. Thanks so much for your reading.

hide-tabbar-in-swiftui's People

Contributors

treattrick avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

hide-tabbar-in-swiftui's Issues

I found another way!

TabBarCustom

There's a .toolbar(.hidden, for: .tabBar) modifier.

Put this modifier in the destination View.

And then

In the ContentView

struct ContentView: View {
    @State private var selection: Int = 0
    
    var body: some View {

        TabView(selection: $selection) {
            HomeView()
            .tabItem {
                Image(systemName: "house")
            }
            .toolbarBackground(.visible, for: .tabBar)
            .tag(0)
            
            NavigationStack {
                ForumView()
            }
            .tabItem {
                Image(systemName: "text.bubble")
            }
            .toolbarBackground(.visible, for: .tabBar)
            .tag(1)
            
            NavigationStack {
                ProjectsView()
            }
            .tabItem {
                Image(systemName: "folder.fill")
            }
            .toolbarBackground(.visible, for: .tabBar)
            .tag(2)

            LoginRootView()
            .tabItem {
                Image(systemName: "person.crop.circle")
            }
            .toolbarBackground(.visible, for: .tabBar)
            .tag(3)
        }
        .tint(Color.orange)
        .animation(.spring(response: 0.3, dampingFraction: 0.6), value: selection)
        .toolbar(.visible, for: .tabBar)        
    }

.animation(.spring(response: 0.3, dampingFraction: 0.6), value: selection)

Put this animation in the TabView's selection property

Get Error Information When Run the Demo

I just cloned this repo and open it using Xcode 12.4, ran it on a simulator iPhone 12 with iOS 14.4. However, once build secceeded, I got the error info below:

2021-03-21 11:39:59.111302+0800 HideTabBarInNavigationView[18305:556332] [LayoutConstraints] Unable to simultaneously satisfy constraints.
	Probably at least one of the constraints in the following list is one you don't want. 
	Try this: 
		(1) look at each constraint and try to figure out which you don't expect; 
		(2) find the code that added the unwanted constraint or constraints and fix it. 
(
    "<NSLayoutConstraint:0x60000016c3c0 'BIB_Trailing_CB_Leading' H:[_UIModernBarButton:0x7fad35f28ba0]-(6)-[_UIModernBarButton:0x7fad35f26940'Tab1']   (active)>",
    "<NSLayoutConstraint:0x60000016c410 'CB_Trailing_Trailing' _UIModernBarButton:0x7fad35f26940'Tab1'.trailing <= BackButton.trailing   (active, names: BackButton:0x7fad35f251f0 )>",
    "<NSLayoutConstraint:0x600000168730 'UINav_static_button_horiz_position' _UIModernBarButton:0x7fad35f28ba0.leading == UILayoutGuide:0x600001b01960'UIViewLayoutMarginsGuide'.leading   (active)>",
    "<NSLayoutConstraint:0x600000168780 'UINavItemContentGuide-leading' H:[BackButton]-(0)-[UILayoutGuide:0x600001b50700'UINavigationBarItemContentLayoutGuide']   (active, names: BackButton:0x7fad35f251f0 )>",
    "<NSLayoutConstraint:0x600000141d10 'UINavItemContentGuide-trailing' UILayoutGuide:0x600001b50700'UINavigationBarItemContentLayoutGuide'.trailing == _UINavigationBarContentView:0x7fad35d13530.trailing   (active)>",
    "<NSLayoutConstraint:0x600000168f00 'UIView-Encapsulated-Layout-Width' _UINavigationBarContentView:0x7fad35d13530.width == 0   (active)>",
    "<NSLayoutConstraint:0x600000165ef0 'UIView-leftMargin-guide-constraint' H:|-(0)-[UILayoutGuide:0x600001b01960'UIViewLayoutMarginsGuide'](LTR)   (active, names: '|':_UINavigationBarContentView:0x7fad35d13530 )>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x60000016c3c0 'BIB_Trailing_CB_Leading' H:[_UIModernBarButton:0x7fad35f28ba0]-(6)-[_UIModernBarButton:0x7fad35f26940'Tab1']   (active)>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.

When I tap Here is Tab1 Tap me to NavigatedView things goes well. However, when return to ContentView (by clickinig <Tab1 on left upper corner), I got another error info printed in the console:

Trying to pop to a missing destination at /Library/Caches/com.apple.xbs/Sources/Monoceros_Sim/Monoceros-120/Shared/NavigationBridge_PhoneTV.swift:341
  • Can I just ignore these errors because the required function (hiding TabBar) goes well?

English is not my native language; please excuse typing errors. Any advice will be helpful!

Hiding TabBar is broken in iOS 15.2

Your exact solution worked over a year ago, but unfortunately, it appears to be broken with the latest updates from Apple. I fear this is not a viable solution for iOS 15. Can you verify this? Or, have I overlooked something?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.