matee
matee

Reputation: 111

How to trigger a TabBar View from another TabBarView with SwiftUI

In SwiftUI, I have created a simple tabbar based view:

struct ContentView: View {
    enum Tab: Int {
        case menu, news, viewc, viewd, viewf
    }
    @State var selectedTab = Tab.menu

    func tabbarItem(text: String, image: String) -> some View {
        VStack {
            Image(systemName: image)
                .imageScale(.large)
            Text(text)
        }
    }

    var body: some View {
         TabView(selection: $selectedTab) {
                     MenuView().tabItem{
                         self.tabbarItem(text: "Menu", image: "list.dash")
                     }.tag(Tab.menu)
                     NewsView().tabItem{
                         self.tabbarItem(text: "News", image: "doc")
                     }.tag(Tab.news)
                     EmptyView().tabItem{
                         self.tabbarItem(text: "ViewC", image: "star.circle")
                     }.tag(Tab.viewc)
                     EmptyView().tabItem{
                         self.tabbarItem(text: "ViewD", image: "speaker")
                     }.tag(Tab.viewd)
                     EmptyView().tabItem{
                         self.tabbarItem(text: "ViewF", image: "person")
                     }.tag(Tab.viewf)
         }
    }
}

Like the screen shown below:

Tab1 Tab2

Questions:

Thanks in advance.

Upvotes: 0

Views: 578

Answers (1)

Felix Marianayagam
Felix Marianayagam

Reputation: 2714

Here's the code I created to achieve the layout you are looking for in SwiftUI. Hope this helps.

import SwiftUI

struct ContentView: View {
    enum Tab: Int {
        case menu, news, viewc, viewd, viewf
    }
    @State var selectedTab = Tab.menu
    @State var showFilter: Bool = false
    @State var selectedFilter: String = ""

    func tabbarItem(text: String, image: String) -> some View {
        VStack {
            Image(systemName: image)
                .imageScale(.large)
            Text(text)
        }
    }

    var body: some View {
        TabView(selection: $selectedTab) {
            NavigationView {
                NewsView(filter: $selectedFilter)
                .navigationBarItems(trailing: Button(action: { self.showFilter.toggle() }) {
                    Text("Filters")
                })
                .navigationBarTitle("News")
                .sheet(isPresented: $showFilter) {
                    MenuView(selectedFilter: self.$selectedFilter)
                }
            }
            .tabItem{
                self.tabbarItem(text: "News", image: "doc")
            }
            .tag(Tab.news)

            ViewC().tabItem{
                self.tabbarItem(text: "ViewC", image: "star.circle")
            }.tag(Tab.viewc)
            EmptyView().tabItem{
                self.tabbarItem(text: "ViewD", image: "speaker")
            }.tag(Tab.viewd)
            EmptyView().tabItem{
                self.tabbarItem(text: "ViewF", image: "person")
            }.tag(Tab.viewf)
        }
    }
}

struct MenuView: View {
    @Environment(\.presentationMode) var presentationMode
    var menus: [String] = ["Menu No.1", "Menu No.2", "Menu No.3"]
    @Binding var selectedFilter: String
    var body: some View {
        List {
            ForEach(menus, id: \.self) { menu in
                Button(action: {
                    self.selectedFilter = menu
                    self.presentationMode.wrappedValue.dismiss()
                }) {
                    Text("\(menu)")
                }
            }
        }
    }
}

struct NewsView: View {
    @Binding var filter: String

    var body: some View {
        List {
            if filter.count > 0 {
                Text("The news is filtered using \(filter)")
            }
            else {
                Text("The latest news is being displayed.")
            }
        }
    }
}

struct ViewC: View {
    var body: some View {
        Text("Under construction")
    }
}

PREVIOUS ANSWER

To achieve the current layout, you need to nest a TabView inside a NavigationView. I created a sample project to test that and below is the code. Though it works, on back button click, I received the below warning:

"Trying to pop to a missing destination at /BuildRoot/Library/Caches/com.apple.xbs/Sources/Monoceros_Sim/Monoceros-39.4.3/UIKit/UIKitNavigationBridge.swift:390"

Therefore, you might need to rethink the application UI. As the menu tab is just the filters for the News tab, have a TabView that combines both the Menu and the News tab into one. On startup the app will show the latest news. You can then have a separate view within NewsView for the filters.

import SwiftUI

struct ContentView: View {
    enum Tab: Int {
        case menu, news, viewc, viewd, viewf
    }
    @State var selectedTab = Tab.menu

    func tabbarItem(text: String, image: String) -> some View {
        VStack {
            Image(systemName: image)
                .imageScale(.large)
            Text(text)
        }
    }

    var body: some View {
        NavigationView {
            TabView(selection: $selectedTab) {
                MenuView().tabItem{
                    self.tabbarItem(text: "Menu", image: "list.dash")
                }.tag(Tab.menu)
                NewsView().tabItem{
                    self.tabbarItem(text: "News", image: "doc")
                }.tag(Tab.news)
                EmptyView().tabItem{
                    self.tabbarItem(text: "ViewC", image: "star.circle")
                }.tag(Tab.viewc)
                EmptyView().tabItem{
                    self.tabbarItem(text: "ViewD", image: "speaker")
                }.tag(Tab.viewd)
                EmptyView().tabItem{
                    self.tabbarItem(text: "ViewF", image: "person")
                }.tag(Tab.viewf)
            }
        }
    }
}

struct MenuView: View {
    var menus: [String] = ["Menu No.1", "Menu No.2", "Menu No.3"]
    var body: some View {
        List {
            ForEach(menus, id: \.self) { menu in
                NavigationLink(destination: NewsView(filter: "filterString")) {
                    Text("\(menu)")
                }
            }
        }
    }
}

struct NewsView: View {
    var filter: String = ""

    var body: some View {
        List {
            if filter.count > 0 {
                Text("The news is filtered using \(filter)")
            }
            else {
                Text("The latest news is being displayed.")
            }
        }
    }
}

Upvotes: 1

Related Questions