swiftuihiddentabviewtabitemswiftui-navigationstack

Creating View inside TabView without showing its icon on tab bar


The main View of the App is TabView with 2 screens.

I need to create an Onboarding View (there are a lot of those) and they are in their own NavigationStack. The Onboarding View shows only at the first launch.

It would be great for design requirements if i could put Onboarding View like item of TabView, with "0" tag, but without icon displaying on tapbar.

I mean, when opens my TabView, there should be no OnboardingView in it(which is .tag(0)). But i can set logic of app to open on .tag(0) on first launch, and .tag(1) on every next.

Of course, i would hide tapBar on OnboardingView.

TabView(selection: $vm.tabSelection,
        content: {
    OnboardingView()
        .tag(0)
    FirstView()
        .tabItem {
            Image(systemName: "sparkles")
            Text("SecondView")
        }
        .tag(1)
    SecondView()
        .navigationTitle("SecondView")
        .tabItem {
            Text("Remove BG")
            Image(systemName: "eraser.line.dashed")
        }
        .tag(2)
}
...

Solution

  • As I was suggesting in a comment, you could switch between the onboarding view and the TabView by using a ZStack as the top-level parent view.

    You said that this wouldn't work because you are using popovers and popover tips in FirstView. The standalone example below demonstrates that in fact it can work.

    The only issue I came across was that the popover tip was not shown with the right width when using a .move transition between onboarding and TabView. But when a (default) opacity transition is used, it works fine.

    struct ContentView: View {
        @State private var onboardingDone = false
        @State private var tabSelection = 1
    
        var body: some View {
            ZStack {
                if onboardingDone {
                    TabView(selection: $tabSelection) {
                        FirstView(onboardingDone: $onboardingDone)
                            .tabItem {
                                Image(systemName: "sparkles")
                                Text("SecondView")
                            }
                            .tag(1)
                        SecondView()
                            .tabItem {
                                Text("Remove BG")
                                Image(systemName: "eraser.line.dashed")
                            }
                            .tag(2)
                    }
                } else {
                    OnboardingView(onboardingDone: $onboardingDone)
                }
            }
        }
    }
    
    struct OnboardingView: View {
        @Binding var onboardingDone: Bool
        var body: some View {
            NavigationStack {
                NavigationLink("Go to next") {
                    VStack(spacing: 50) {
                        Text("Onboarding completed")
                        Button("Ready for FirstView") {
                            withAnimation {
                                onboardingDone = true
                            }
                        }
                        .buttonStyle(.bordered)
                    }
                }
                .navigationTitle("OnboardingView")
            }
        }
    }
    
    struct FirstView: View {
        @Binding var onboardingDone: Bool
    
        struct QuickTip: Tip {
            var title: Text {
                Text("Want to onboard again?")
            }
    
            var message: Text? {
                Text("The button to reset the onboarding is in the next view")
            }
    
            var image: Image? {
                Image(systemName: "hand.thumbsup.circle")
            }
        }
    
        var body: some View {
            NavigationStack {
                NavigationLink("Go to next") {
                    VStack(spacing: 50) {
                        Text("Child of FirstView")
                        Button("Reset onboarding") {
                            withAnimation {
                                onboardingDone = false
                            }
                        }
                        .buttonStyle(.bordered)
                    }
                }
                .popoverTip(QuickTip())
                .onAppear {
                    try? Tips.resetDatastore()
                    try? Tips.configure()
                }
                .navigationTitle("FirstView")
            }
        }
    }
    
    struct SecondView: View {
        @State private var isShowingPopover = false
    
        var body: some View {
            NavigationStack {
                NavigationLink("Go to next") {
                    VStack(spacing: 50) {
                        Text("Child of SecondView with Popover")
                            .onTapGesture {
                                isShowingPopover.toggle()
                            }
                            .popover(isPresented: $isShowingPopover, arrowEdge: .bottom) {
                                Text("Interesting popover")
                                    .padding()
                                    .presentationCompactAdaptation(.popover)
                            }
                    }
                }
                .navigationTitle("SecondView")
            }
        }
    }
    

    Animation