如何在 SwiftUI 中将呈现视图关闭到选项卡视图的根视图?
How to dismiss a presenting view to the root view of tab view in SwiftUI?
我在主页上使用 TabView
。假设我有 4 个标签。
在第二个选项卡上,我可以使用 NavigationLink
转到另一个视图,然后使用 NavigationLink
转到另外两个视图。然后在最新的视图上,有一个按钮可以显示一个视图,我使用 .fullScreenCover
(因为我想全屏显示)。
在呈现视图中,我在 navigationBarItems
的左侧添加了一个 X
标记以关闭。我使用 @Environment(\.presentationMode) var presentationMode
和 presentationMode.wrappedValue.dismiss()
来解雇。但它只会将呈现视图关闭到前一个视图,而实际上我想将它关闭到我的视图的根目录,即我的 TabView
.
的第二个选项卡
有办法吗?因为我查阅了一些文章,但没有相关内容,尤其是在 TabView
上下文中。
我也有一个问题:
- 使用
.fullScreenCover
是否正确?或者是否有另一种可能的解决方案,例如呈现全屏样式的模式(如果有任何原因我也不确定)。
如有任何建议,我们将不胜感激,提前致谢。
您有 2 个选择:
使用 .fullScreenCover
你将有一个绑定,导致它被呈现,你可以将这个绑定传递给内容,当用户点击 x
设置为假
您可以使用 @Environment(\.presentationMode) var presentationMode
然后在您的按钮主体中调用 presentationMode.wrappedValue.dismiss()
。
编辑:
如果您想完全放松,您应该使 TabView 成为基于绑定的。我喜欢使用 SceneStorage
看看这个 post 然后你可以在你的应用程序中的任何地方访问这个 SceneStorage
值来响应它,也可以更新和更改导航(这个还可以为您提供适当的状态恢复!)
如果您以这种方式制作 TabView:
struct ContentView: View {
@SceneStorage("selectedTab") var selectedTab: Tab = .car
var body: some View {
TabView(selection: $selectedTab) {
CarTrips()
.tabItem {
Image(systemName: "car")
Text("Car Trips")
}.tag(Tab.car)
TramTrips()
.tabItem {
Image(systemName: "tram.fill")
Text("Tram Trips")
}.tag(Tab.tram)
AirplaneTrips()
.tabItem {
Image(systemName: "airplane")
Text("Airplane Trips")
}.tag(Tab.airplaine)
}
}
}
enum Tab: String {
case car
case tram
case airplaine
}
然后在您的应用程序中您想要更改导航的地方创建一个按钮视图。
struct ViewCarButton: View {
@SceneStorage("selectedTab") var selectedTab: Tab = .car
var body: some View {
Button("A Button") {
selectedTab = .car
}
}
}
这将强制选择的标签成为 car
标签。
如果您不想更改选项卡而是更改导航视图导航到的内容,您可以为此使用相同的概念,NavigationLink 是一个绑定,如果此绑定是使用 @SceneStorage
然后在您的 ViewCarButton
中,您可以对其进行更改以更改导航状态。
presentationMode
是一级效果值,即改变它你关闭一个当前显示的屏幕。
因此,要关闭许多显示的屏幕,您必须以编程方式实现它,如下面的演示所示。
可能的方法是使用自定义 EnvironmentKey
将其向下传递到视图层次结构 w/o 每个级别视图的紧密耦合(如绑定)和 inject/call 仅在该级别需要。
Demo 使用 Xcode 12.4 / iOS 14.4
测试
struct ContentView: View {
var body: some View {
TabView {
Text("Tab1")
.tabItem { Image(systemName: "1.square") }
Tab2RootView()
.tabItem { Image(systemName: "2.square") }
}
}
}
struct Tab2RootView: View {
@State var toRoot = false
var body: some View {
NavigationView {
Tab2NoteView(level: 0)
.id(toRoot) // << reset to root !!
}
.environment(\.rewind, $toRoot) // << inject here !!
}
}
struct Tab2NoteView: View {
@Environment(\.rewind) var rewind
let level: Int
@State private var showFullScreen = false
var body: some View {
VStack {
Text(level == 0 ? "ROOT" : "Level \(level)")
NavigationLink("Go Next", destination: Tab2NoteView(level: level + 1))
Divider()
Button("Full Screen") { showFullScreen.toggle() }
.fullScreenCover(isPresented: $showFullScreen,
onDismiss: { rewind.wrappedValue.toggle() }) {
Tab2FullScreenView()
}
}
}
}
struct RewindKey: EnvironmentKey {
static let defaultValue: Binding<Bool> = .constant(false)
}
extension EnvironmentValues {
var rewind: Binding<Bool> {
get { self[RewindKey.self] }
set { self[RewindKey.self] = newValue }
}
}
struct Tab2FullScreenView: View {
@Environment(\.presentationMode) var mode
var body: some View {
Button("Close") { mode.wrappedValue.dismiss() }
}
}
我在主页上使用 TabView
。假设我有 4 个标签。
在第二个选项卡上,我可以使用 NavigationLink
转到另一个视图,然后使用 NavigationLink
转到另外两个视图。然后在最新的视图上,有一个按钮可以显示一个视图,我使用 .fullScreenCover
(因为我想全屏显示)。
在呈现视图中,我在 navigationBarItems
的左侧添加了一个 X
标记以关闭。我使用 @Environment(\.presentationMode) var presentationMode
和 presentationMode.wrappedValue.dismiss()
来解雇。但它只会将呈现视图关闭到前一个视图,而实际上我想将它关闭到我的视图的根目录,即我的 TabView
.
有办法吗?因为我查阅了一些文章,但没有相关内容,尤其是在 TabView
上下文中。
我也有一个问题:
- 使用
.fullScreenCover
是否正确?或者是否有另一种可能的解决方案,例如呈现全屏样式的模式(如果有任何原因我也不确定)。
如有任何建议,我们将不胜感激,提前致谢。
您有 2 个选择:
使用
.fullScreenCover
你将有一个绑定,导致它被呈现,你可以将这个绑定传递给内容,当用户点击x
设置为假您可以使用
@Environment(\.presentationMode) var presentationMode
然后在您的按钮主体中调用presentationMode.wrappedValue.dismiss()
。
编辑:
如果您想完全放松,您应该使 TabView 成为基于绑定的。我喜欢使用 SceneStorage
看看这个 post 然后你可以在你的应用程序中的任何地方访问这个 SceneStorage
值来响应它,也可以更新和更改导航(这个还可以为您提供适当的状态恢复!)
如果您以这种方式制作 TabView:
struct ContentView: View {
@SceneStorage("selectedTab") var selectedTab: Tab = .car
var body: some View {
TabView(selection: $selectedTab) {
CarTrips()
.tabItem {
Image(systemName: "car")
Text("Car Trips")
}.tag(Tab.car)
TramTrips()
.tabItem {
Image(systemName: "tram.fill")
Text("Tram Trips")
}.tag(Tab.tram)
AirplaneTrips()
.tabItem {
Image(systemName: "airplane")
Text("Airplane Trips")
}.tag(Tab.airplaine)
}
}
}
enum Tab: String {
case car
case tram
case airplaine
}
然后在您的应用程序中您想要更改导航的地方创建一个按钮视图。
struct ViewCarButton: View {
@SceneStorage("selectedTab") var selectedTab: Tab = .car
var body: some View {
Button("A Button") {
selectedTab = .car
}
}
}
这将强制选择的标签成为 car
标签。
如果您不想更改选项卡而是更改导航视图导航到的内容,您可以为此使用相同的概念,NavigationLink 是一个绑定,如果此绑定是使用 @SceneStorage
然后在您的 ViewCarButton
中,您可以对其进行更改以更改导航状态。
presentationMode
是一级效果值,即改变它你关闭一个当前显示的屏幕。
因此,要关闭许多显示的屏幕,您必须以编程方式实现它,如下面的演示所示。
可能的方法是使用自定义 EnvironmentKey
将其向下传递到视图层次结构 w/o 每个级别视图的紧密耦合(如绑定)和 inject/call 仅在该级别需要。
Demo 使用 Xcode 12.4 / iOS 14.4
测试struct ContentView: View {
var body: some View {
TabView {
Text("Tab1")
.tabItem { Image(systemName: "1.square") }
Tab2RootView()
.tabItem { Image(systemName: "2.square") }
}
}
}
struct Tab2RootView: View {
@State var toRoot = false
var body: some View {
NavigationView {
Tab2NoteView(level: 0)
.id(toRoot) // << reset to root !!
}
.environment(\.rewind, $toRoot) // << inject here !!
}
}
struct Tab2NoteView: View {
@Environment(\.rewind) var rewind
let level: Int
@State private var showFullScreen = false
var body: some View {
VStack {
Text(level == 0 ? "ROOT" : "Level \(level)")
NavigationLink("Go Next", destination: Tab2NoteView(level: level + 1))
Divider()
Button("Full Screen") { showFullScreen.toggle() }
.fullScreenCover(isPresented: $showFullScreen,
onDismiss: { rewind.wrappedValue.toggle() }) {
Tab2FullScreenView()
}
}
}
}
struct RewindKey: EnvironmentKey {
static let defaultValue: Binding<Bool> = .constant(false)
}
extension EnvironmentValues {
var rewind: Binding<Bool> {
get { self[RewindKey.self] }
set { self[RewindKey.self] = newValue }
}
}
struct Tab2FullScreenView: View {
@Environment(\.presentationMode) var mode
var body: some View {
Button("Close") { mode.wrappedValue.dismiss() }
}
}