【问题标题】:SwiftUI: resetting TabViewSwiftUI:重置 TabView
【发布时间】:2021-07-06 18:50:52
【问题描述】:

我有一个TabView,在SwiftUI 生命周期应用程序中有两个选项卡,其中一个具有复杂的视图结构:NavigationView,里面有很多子视图,即:NavigationLinks 和他们的@987654328 @s 分布在视图树的多个层次上,每个子视图本身就是另一个具有sheets 和/或其他DestinationViews 的视图层次结构。在这个层次结构中的某个时刻,我想将TabView 重置为其显示第一个最多视图的原始状态,以便用户可以在该状态下重新开始他们的旅程,就像他们第一次打开应用程序一样,因此很难找到类似isActiveisPresented 的绑定来弹出或关闭视图和工作表。

我想将 TabView 包装在另一个视图中:RootView 试图找到一种简单的方法来从头开始重新创建 TabView 或类似 refreshing / resetting TabView,但找不到如何操作的提示。

这是我的代码 sn-p:

@main
struct TestApp: App {
    var body: some Scene {
        WindowGroup {
            RootView()
        }
    }
}

struct RootView: View {
    var body: some View {
        ContentView()
    }
}

struct ContentView: View {
    var body: some View {
        TabView { // <-- I need to reset it to its original state
           View1() // <---- this view has complex view hierarchy
            .tabItem {
                Text("Home")
                
            }.tag(0)
            
            View2()
            .tabItem {
                Text("Settings")
            }.tag(1)
        }
    }
}

附言我不是在寻找“将视图弹出到根视图”,因为当用户可能打开其中一张工作表并开始的许多活动NavigationLink 目的地时,这是无法完成的工作表内的新导航旅程。

****** 更新 ******

我创建了一个新的Environment 值来保存一个布尔值,该值应该指示TabView 是否应该重置,并且我已经跟踪每个视图中的每个isPresentedisActive 状态变量并重置一旦将环境值设置为true,就像这样:

struct ResetTabView: EnvironmentKey {
static var defaultValue: Binding<ResetTabObservable> = .constant(ResetTabObservable())
 }
 extension EnvironmentValues {
var resetTabView: Binding<ResetTabObservable> {
    get { self[ResetTabView.self] }
    set { self[ResetTabView.self] = newValue }
   }
 }

 class ResetTabObservable: ObservableObject {
    @Published var newValue = false
}

在每个将显示工作表或推送新视图的视图中,我添加了如下内容:

struct View3: View {
    @State var showSheet = false
    @Environment(\.resetTabView) var reset
    
    var body: some View {
        Text("This is view 3")
        Button(action: {
            showSheet = true
        }, label: {
            Text("show view 4")
        })
        .sheet(isPresented: $showSheet) {
            View4()
        }
        .onReceive(reset.$newValue.wrappedValue, perform: { val in
            if val == true {
                showSheet = false
            }
        })
    }
}

在最后一个视图中(这将重置TabView),我像这样切换Environment 值:

struct View5: View {
    @Environment(\.resetTabView) var reset
    
    var body: some View {
        VStack {
            Text("This is view 5")
            Button(action: {
                reset.newValue.wrappedValue = true
            }, label: {
                Text("reset tab view")
            })
        }
    }
}

这导致视图被尴尬地驳回:

【问题讨论】:

  • 这是否回答了您的问题stackoverflow.com/a/67015765/12299030? (参见关于reset to root的地方)
  • 不是我想要的,我添加了一个 p.s.在上面的问题中,我不是在寻找“关闭或弹出到根视图”

标签: swift swiftui swiftui-tabview


【解决方案1】:

我为此做的是使用@SceneStorage("key")(而不是@State)存储我的所有演示文稿绑定,这样他们不仅尊重状态恢复!但是您也可以使用相同的密钥轻松地在整个应用程序中访问它们。 This post 提供了一个很好的例子,说明如何在 iPad 上实现从 Tab 切换到 Sidebar 视图。

我在我的应用程序中使用了它,因此如果我有一个按钮或需要展开许多演示文稿的东西,它可以读取所有这些值并将它们重置为想要的值,而无需传递大量绑定。

【讨论】:

  • 这不会将大量打开的页面重置回第一个选项卡视图,我想实现这一点的最佳方法是创建一个全新的RootView,其中包含一个新的TabView
  • 如果您对每个演示文稿都有一个SceneStorage,那么您可以在您的按钮中将它们全部重置为默认状态。 (您确实需要通过 yes 明确设置所有这些)