Is there any way to pop to root view by tapping the Tab Bar like most iOS apps, in SwiftUI?
Here's an example of the expected behavior.
I've tried to programmatically pop views using simultaneousGesture
as follow:
import SwiftUI
struct TabbedView: View {
@State var selection = 0
@Environment(\.presentationMode) var presentationMode
var body: some View {
TabView(selection: $selection) {
RootView()
.tabItem {
Image(systemName: "house")
.simultaneousGesture(TapGesture().onEnded{
self.presentationMode.wrappedValue.dismiss()
print("View popped")
})
}.tag(0)
Text("")
.tabItem {
Image(systemName: "line.horizontal.3")
}.tag(1)
}
}
}
struct RootView: View {
var body: some View{
NavigationView{
NavigationLink(destination:SecondView()){
Text("Go to second view")
}
}
}
}
struct SecondView: View {
var body: some View{
Text("Tapping the house icon should pop back to root view")
}
}
But seems like those gestures were ignored.
Any suggestions or solutions are greatly appreciated
Press Cmd+N to create a new SwiftUI View, calling it “MainView”. Creating tabs is as easy as putting different views inside an instance of TabView , but in order to add an image and text to the tab bar item of each view we need to use the tabItem() modifier.
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 .
I messed around with this for a while and this works great. I combined answers from all over and added some stuff of my own. I'm a beginner at Swift so feel free to make improvements.
Here's a demo.
This view has the NavigationView.
import SwiftUI
struct AuthenticatedView: View {
@StateObject var tabState = TabState()
var body: some View {
TabView(selection: $tabState.selectedTab) {
NavigationView {
NavigationLink(destination: TestView(titleNum: 0), isActive: $tabState.showTabRoots[0]) {
Text("GOTO TestView #1")
.padding()
.foregroundColor(Color.white)
.frame(height:50)
.background(Color.purple)
.cornerRadius(8)
}
.navigationTitle("")
.navigationBarTitleDisplayMode(.inline)
}
.navigationViewStyle(.stack)
.onAppear(perform: {
tabState.lastSelectedTab = TabState.Tab.first
}).tabItem {
Label("First", systemImage: "list.dash")
}.tag(TabState.Tab.first)
NavigationView {
NavigationLink(destination: TestView(titleNum: 0), isActive: $tabState.showTabRoots[1]) {
Text("GOTO TestView #2")
.padding()
.foregroundColor(Color.white)
.frame(height:50)
.background(Color.purple)
.cornerRadius(8)
}.navigationTitle("")
.navigationBarTitleDisplayMode(.inline).navigationBarTitle(Text(""), displayMode: .inline)
}
.navigationViewStyle(.stack)
.onAppear(perform: {
tabState.lastSelectedTab = TabState.Tab.second
}).tabItem {
Label("Second", systemImage: "square.and.pencil")
}.tag(TabState.Tab.second)
}
.onReceive(tabState.$selectedTab) { selection in
if selection == tabState.lastSelectedTab {
tabState.showTabRoots[selection.rawValue] = false
}
}
}
}
struct AuthenticatedView_Previews: PreviewProvider {
static var previews: some View {
AuthenticatedView()
}
}
class TabState: ObservableObject {
enum Tab: Int, CaseIterable {
case first = 0
case second = 1
}
@Published var selectedTab: Tab = .first
@Published var lastSelectedTab: Tab = .first
@Published var showTabRoots = Tab.allCases.map { _ in
false
}
}
This is my child view
import SwiftUI
struct TestView: View {
let titleNum: Int
let title: String
init(titleNum: Int) {
self.titleNum = titleNum
self.title = "TestView #\(titleNum)"
}
var body: some View {
VStack {
Text(title)
NavigationLink(destination: TestView(titleNum: titleNum + 1)) {
Text("Goto View #\(titleNum + 1)")
.padding()
.foregroundColor(Color.white)
.frame(height:50)
.background(Color.purple)
.cornerRadius(8)
}
NavigationLink(destination: TestView(titleNum: titleNum + 100)) {
Text("Goto View #\(titleNum + 100)")
.padding()
.foregroundColor(Color.white)
.frame(height:50)
.background(Color.purple)
.cornerRadius(8)
}
.navigationTitle(title)
.navigationBarTitleDisplayMode(.inline)
}
}
}
struct TestView_Previews: PreviewProvider {
static var previews: some View {
TestView(titleNum: 0)
}
}
We can use tab bar selection binding to get the selected index. On this binding we can check if the tab is already selected then pop to root for navigation on selection.
struct ContentView: View {
@State var showingDetail = false
@State var selectedIndex:Int = 0
var selectionBinding: Binding<Int> { Binding(
get: {
self.selectedIndex
},
set: {
if $0 == self.selectedIndex && $0 == 0 && showingDetail {
print("Pop to root view for first tab!!")
showingDetail = false
}
self.selectedIndex = $0
}
)}
var body: some View {
TabView(selection:selectionBinding) {
NavigationView {
VStack {
Text("First View")
NavigationLink(destination: DetailView(), isActive: $showingDetail) {
Text("Go to detail")
}
}
}
.tabItem { Text("First") }.tag(0)
Text("Second View")
.tabItem { Text("Second") }.tag(1)
}
}
}
struct DetailView: View {
var body: some View {
Text("Detail")
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With