我有一个简单的TabView:
TabView {
NavigationView {
VStack {
NavigationLink(destination: Text("Detail")) {
Text("Go to detail")
}
}
}
.tabItem { Text("First") }
.tag(0)
Text("Second View")
.tabItem { Text("Second") }
.tag(1)
}
当我转到选项卡1上的详细信息视图时,切换到选项卡2,然后再切换回选项卡1,我假设将返回到详细信息视图(iOS中随处可见的基本UX)。而是重置为选项卡1的根视图。
由于SwiftUI似乎不支持此功能,我该如何解决?
答案 0 :(得分:10)
这里不太明显的解决方案是实际上不使用SwiftUI。为了获得UIKit行为,我将UIKit UITabBarController
包裹在SwiftUI UIViewControllerRepresentable
中,例如以下示例:https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit。
我在这里展示一个基本的实现。完整的最新实现在github上:https://gist.github.com/Amzd/2eb5b941865e8c5cccf149e6e07c8810
在SwiftUI视图中包装UIKit UITabBarController:
struct UIKitTabView: View {
var viewControllers: [UIHostingController<AnyView>]
init(_ tabs: [Tab]) {
self.viewControllers = tabs.map {
let host = UIHostingController(rootView: $0.view)
host.tabBarItem = $0.barItem
return host
}
}
var body: some View {
TabBarController(controllers: viewControllers)
.edgesIgnoringSafeArea(.all)
}
struct Tab {
var view: AnyView
var barItem: UITabBarItem
init<V: View>(view: V, barItem: UITabBarItem) {
self.view = AnyView(view)
self.barItem = barItem
}
}
}
struct TabBarController: UIViewControllerRepresentable {
var controllers: [UIViewController]
func makeUIViewController(context: Context) -> UITabBarController {
let tabBarController = UITabBarController()
tabBarController.viewControllers = controllers
return tabBarController
}
}
用法示例:
struct ExampleView: View {
@State var text: String = ""
var body: some View {
UIKitTabView([
UIKitTabView.Tab(
view: NavView(),
barItem: UITabBarItem(title: "First", image: nil, selectedImage: nil)
),
UIKitTabView.Tab(
view: Text("Second View"),
barItem: UITabBarItem(title: "Second", image: nil, selectedImage: nil)
)
])
}
}
struct NavView: View {
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: Text("This page stays when you switch back and forth between tabs (as expected on iOS)")) {
Text("Go to detail")
}
}
}
}
}
答案 1 :(得分:1)
因此,这会在切换选项卡时“保留”详细信息视图,但只能通过在切换回选项卡1时明显地推动详细信息视图来实现。我无法通过.animation()
来禁用它。
此外,您几乎必须覆盖DetailView
中的导航栏项,因为默认的后退按钮的行为很奇怪(注释.navigationBarItems()
行以了解我的意思)。>
有了这些警告,这确实可以解决。
struct ContentView: View {
@State var showingDetail = false
var body: some View {
TabView {
NavView(showingDetail: $showingDetail)
.tabItem { Text("First") }
.tag(0)
Text("Second View")
.tabItem { Text("Second") }
.tag(1)
}
}
}
struct NavView: View {
@Binding var showingDetail: Bool
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: DetailView(showing: $showingDetail), isActive: $showingDetail) {
Text("Go to detail")
}
}
}
}
}
struct DetailView: View {
@Binding var showing: Bool
var body: some View {
Text("Detail")
.navigationBarItems(leading: Button("Back", action: { self.showing = false }))
}
}
答案 2 :(得分:0)
这是一个简单的示例,说明如何保留导航堆栈的状态,其根目录为项目列表:
struct ContentView: View {
var body: some View {
TabView {
Text("First tab")
.tabItem { Image(systemName: "1.square.fill"); Text("First") }
.tag(0)
SecondTabView()
.tabItem { Image(systemName: "2.square.fill"); Text("Second") }
.tag(1)
}
}
}
struct SecondTabView: View {
private struct ListItem: Identifiable {
var id = UUID()
let title: String
}
private let items = (1...10).map { ListItem(title: "Item #\($0)") }
@State var selectedItemIndex: Int? = nil
var body: some View {
NavigationView {
List(self.items.indices) { index in
NavigationLink(destination: Text(self.items[index].title),
tag: index,
selection: self.$selectedItemIndex) {
Text(self.items[index].title)
}
}
.navigationBarTitle("Second tab", displayMode: .inline)
}
}
}