在 SwiftUI 中使用选项卡栏弹出到根视图

Ngu*_*Hào 10 swift swiftui

在 SwiftUI 中,有没有办法像大多数 iOS 应用程序一样通过点击选项卡栏来弹出到根视图?

这是预期行为的示例。

在此输入图像描述

我尝试使用simultaneousGesture以下方式以编程方式弹出视图:

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")
    }
}
Run Code Online (Sandbox Code Playgroud)

但似乎这些手势被忽略了。

任何建议或解决方案将不胜感激

小智 9

我们可以使用标签栏选择绑定来获取选定的索引。在此绑定上,我们可以检查选项卡是否已被选中,然后弹出到根目录以便在选择时进行导航。

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")
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢你!我为此等了2年!您必须为每个“NavigationLink”创建一个唯一的“isActive”布尔值,但这有时会很烦人。 (2认同)

小智 5

我用这个搞乱了一段时间,效果很好。我结合了各地的答案,并添加了一些我自己的东西。我是 Swift 的初学者,所以请随时进行改进。

这是一个演示。

在此输入图像描述

该视图有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
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的孩子视角

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)
    }
}
Run Code Online (Sandbox Code Playgroud)


pro*_*asm 4

在我自己的应用程序中,我使用更改最顶层视图上的 .id 修饰符的方法,这会导致 NavigationView 弹出到根目录。为了清楚起见,我还将这种导航弹出行为分解到它自己的包装视图“TabbedNavView”中,如下所示。

import SwiftUI

class TabMonitor: ObservableObject {
    @Published var selectedTab = 1
}

struct ContentView: View {
    @StateObject private var tabMonitor = TabMonitor()
    
    var body: some View {
        TabView(selection: $tabMonitor.selectedTab) {
            TabbedNavView(tag: 1) {
                DetailView(index: 1)
            }
            .tabItem { Label("Tab1", systemImage: "book") }
            .tag(1)
            
            TabbedNavView(tag: 2) {
                DetailView(index: 10)
            }
            .tabItem { Label("Tab2", systemImage: "wrench") }
            .tag(2)
        } //TabView
        .environmentObject(tabMonitor)
    } //body
} //ContentView


struct DetailView: View {
    var index: Int
    
    var body: some View {
        NavigationLink(
            destination: DetailView(index: index + 1)
        ) {
            Text("Detail \(index)")
        }
    } //body
} //DetailView

struct TabbedNavView: View {
    @EnvironmentObject var tabMonitor: TabMonitor
    
    private var tag: Int
    private var content: AnyView

    init(
        tag: Int,
        @ViewBuilder _ content: () -> any View
    ) {
        self.tag = tag
        self.content = AnyView(content())
    } //init(tag:content:)

    @State private var id = 1
    @State private var selected = false

    var body: some View {
        NavigationView {
            content
                .id(id)
                .onReceive(tabMonitor.$selectedTab) { selection in
                    if selection != tag {
                        selected = false
                    } else {
                        if selected {
                            id *= -1 //id change causes pop to root
                        }

                        selected = true
                    }
                } //.onReceive
        } //NavigationView
        .navigationViewStyle(.stack)
    } //body
} //TabbedNavView
Run Code Online (Sandbox Code Playgroud)

这种方法的好处是它可以处理任意数量的选项卡以及每个选项卡中任意数量的 NavigationLink。每个 TabbedNavView 都会跟踪自己是否是选定的选项卡,并且每当连续点击选项卡两次时,都会将 NavigationView 弹出到根(通过翻转根视图的 .id 修饰符的符号)。

编辑:适用于 iOS 15+。