SwiftUI - 如何关闭工作表视图,同时关闭该视图
Posted
技术标签:
【中文标题】SwiftUI - 如何关闭工作表视图,同时关闭该视图【英文标题】:SwiftUI - How to close the sheet view, while dismissing that view 【发布时间】:2020-06-04 09:28:26 【问题描述】:我要实现的功能。例如,来自 Apple 的“查找”视图。
我的目标是当工作表视图通过导航推送另一个视图时,用户可以点击导航项按钮来关闭工作表视图。比如,下面这个 gif。
我尝试实现这个功能。
我发现了用户点击“完成”按钮时的问题。该应用程序不会关闭工作表视图。它只会将视图弹出到父视图。比如,下面这个 gif。
这是我的代码。
import SwiftUI
struct ContentView: View
@State var isShowSheet = false
var body: some View
Button(action:
self.isShowSheet.toggle()
)
Text("Tap to show the sheet")
.sheet(isPresented: $isShowSheet)
NavView()
struct NavView: View
var body: some View
NavigationView
NavigationLink(destination: NavSubView())
Text("Enter Sub View")
.navigationViewStyle(StackNavigationViewStyle())
struct NavSubView: View
@Environment(\.presentationMode) var presentationMode
var body: some View
Text("Hello")
.navigationBarItems(trailing:
Button(action:
self.presentationMode.wrappedValue.dismiss()
)
Text("Done")
)
我是如何实现这个功能的? :) 请帮帮我,谢谢。 :)
【问题讨论】:
【参考方案1】:更新: 恢复的原始版本 - 应按预期对主题启动器的代码进行以下更改。经测试可与 Xcode 13 / ios 15 配合使用
由于工作表中的导航可能足够长并且关闭可能不是在所有导航子视图中,我更喜欢使用环境来仅在需要的地方指定关闭功能,而不是通过所有导航堆栈传递绑定。
这是可能的方法(使用 Xcode 11.2 / iOS 13.2 测试)
-
定义环境键来存储工作表状态
struct ShowingSheetKey: EnvironmentKey
static let defaultValue: Binding<Bool>? = nil
extension EnvironmentValues
var showingSheet: Binding<Bool>?
get self[ShowingSheetKey.self]
set self[ShowingSheetKey.self] = newValue
-
将此环境值设置为工作表内容的根目录,以便在声明时在任何子视图中都可用
.sheet(isPresented: $isShowSheet)
NavView()
.environment(\.showingSheet, self.$isShowSheet)
-
仅在将要使用的子视图中声明和使用环境值
struct NavSubView: View
@Environment(\.showingSheet) var showingSheet
var body: some View
Text("Hello")
.navigationBarItems(trailing:
Button("Done")
self.showingSheet?.wrappedValue = false
)
【讨论】:
Asperi,您的代码不起作用,无法编译。我修复了它(在我的第二次编辑中)。 我知道你为什么不接受我的编辑 - 它添加了有价值的附加(以前丢失!)代码并修复了拼写错误/改进了英语,从而使其更容易理解和减少混乱。【参考方案2】:我从未尝试过 SwiftUI,但我来自 UIKit + RxSwift,所以我知道绑定是如何工作的。我从 SwiftUI 教程中阅读了很多示例代码,您关闭模式的方式实际上是正确的,但显然不适用于导航堆栈。
我刚才学到的一种方法是使用@Binding
var。这可能不是最好的解决方案,但它确实有效!
所以你的ContentView
中有这个$isShowSheet
。通过在 NavView
中声明一个变量,将该对象传递给您的 NavView
结构。
内容视图
.....
.sheet(isPresented: $isShowSheet)
NavView(isShowSheet: self.$isShowSheet)
导航视图
struct NavView: View
@Binding var isShowSheet: Bool
var body: some View
NavigationView
NavigationLink(destination: NavSubView(isShowSheet: self.$isShowSheet))
Text("Enter Sub View")
.navigationViewStyle(StackNavigationViewStyle())
最后,对你的 subView 做同样的事情。
NavSubView
struct NavSubView: View
@Environment(\.presentationMode) var presentationMode
@Binding var isShowSheet: Bool
var body: some View
Text("Hello")
.navigationBarItems(trailing:
Button(action:
//self.presentationMode.projectedValue.wrappedValue.dismiss()
self.isShowSheet = false
)
Text("Done")
)
现在您可以看到,您只需向 isShowSheet
绑定变量 - false
发送一个新信号。
self.isShowSheet = false
瞧!
【讨论】:
【参考方案3】:这是 Asperi 上面代码的改进版本,因为他们不会接受我的编辑。主要功劳归于他们。
由于工作表中的导航可能足够长并且关闭可能不是在所有导航子视图中,我更喜欢使用环境来仅在需要的地方指定关闭功能,而不是通过所有导航堆栈传递绑定。
这是可能的方法(使用 Xcode 13 / iOS 15 测试)
定义环境键来存储工作表状态
struct ShowingSheetKey: EnvironmentKey
static let defaultValue: Binding<Bool>? = nil
extension EnvironmentValues
var isShowingSheet: Binding<Bool>?
get self[ShowingSheetKey.self]
set self[ShowingSheetKey.self] = newValue
将此环境值设置为工作表内容的根,因此在声明时它将在任何子视图中可用
@State var isShowingSheet = false
...
Button("open sheet")
isShowingSheet?.wrappedValue = true
// note no $ in front of isShowingSheet
.sheet(isPresented: isShowingSheet ?? .constant(false))
NavView()
.environment(\.isShowingSheet, self.$isShowingSheet)
仅在将要使用的子视图中声明和使用环境值
struct NavSubView: View
@Environment(\.isShowingSheet) var isShowingSheet
var body: some View
Text("Hello")
.navigationBarItems(trailing:
Button("Done")
isShowingSheet?.wrappedValue = false
)
【讨论】:
这段代码不起作用,State 不是 Binding,请您检查一下吗? 您的问题到底是什么?看到错误消息?像这样对我来说很好。 @布鲁诺以上是关于SwiftUI - 如何关闭工作表视图,同时关闭该视图的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI:.sheet() 在关闭当前工作表时不会使用预期数据转到上一个视图
从 NavigationView 呈现的 SwiftUI 关闭模式表(Xcode Beta 5)