SwiftUI - 如何关闭 sheet 视图,同时关闭该视图
SwiftUI - How to close the sheet view, while dismissing that view
我要实现功能。比如,"Look up" 来自 Apple 的视图。
我的目标是当 sheet 视图通过导航推送另一个视图时,用户可以点击导航项按钮关闭 sheet 视图。就像下面的 gif。
我尝试实现这个功能
我发现了一个问题,即当用户点击 "Done" 按钮时。该应用程序不会关闭 sheet 视图。它只会将视图弹出到父视图。就像下面的 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")
}
)
}
}
这个功能是怎么实现的? :)
请帮助我,谢谢。 :)
我从来没有尝试过 SwiftUI,但我来自 UIKit + RxSwift,所以我知道绑定是如何工作的。我从 SwiftUI 教程中阅读了相当多的示例代码,您关闭模态的方式实际上是正确的,但显然不适用于导航堆栈。
我刚才学到的一种方法是使用 @Binding
变量。这可能不是最好的解决方案,但它奏效了!
所以你的 ContentView
中有这个 $isShowSheet
。通过在 NavView
.
中声明一个变量,将该对象传递给您的 NavView
结构
ContentView
.....
}.sheet(isPresented: $isShowSheet) {
NavView(isShowSheet: self.$isShowSheet)
}
NavView
struct NavView: View {
@Binding var isShowSheet: Bool
var body: some View {
NavigationView {
NavigationLink(destination: NavSubView(isShowSheet: self.$isShowSheet)) {
Text("Enter Sub View")
}
} .navigationViewStyle(StackNavigationViewStyle())
}
}
最后,对您的子视图执行相同的操作。
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
瞧!
更新: 恢复原始版本 - 应按预期对主题发起人的代码进行以下更改。测试与 Xcode 13 / iOS 15
由于 sheet 中的导航可能足够长并且不能在所有导航子视图中关闭,我更喜欢使用环境来仅在需要的地方指定关闭功能,而不是通过所有导航传递绑定堆叠。
这是可能的方法(使用 Xcode 11.2 / iOS 13.2 测试)
- 定义存储sheet状态的环境密钥
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 内容的根目录,因此在声明时它将在任何子视图中可用
}.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 代码的改进版本,因为他们不接受我的编辑。主要归功于他们。
由于 sheet 中的导航可能足够长并且不能在所有导航子视图中关闭,我更喜欢使用环境来仅在需要的地方指定关闭功能,而不是通过所有导航传递绑定堆叠.
这是可能的方法(使用 Xcode 13 / iOS 15 测试)
定义存储sheet状态的环境密钥
struct ShowingSheetKey: EnvironmentKey {
static let defaultValue: Binding<Bool>? = nil
}
extension EnvironmentValues {
var isShowingSheet: Binding<Bool>? {
get { self[ShowingSheetKey.self] }
set { self[ShowingSheetKey.self] = newValue }
}
}
将此环境值设置为 sheet 内容的根目录,因此在声明时它将在任何子视图中可用
@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
}
)
}
}
我要实现功能。比如,"Look up" 来自 Apple 的视图。
我的目标是当 sheet 视图通过导航推送另一个视图时,用户可以点击导航项按钮关闭 sheet 视图。就像下面的 gif。
我尝试实现这个功能
我发现了一个问题,即当用户点击 "Done" 按钮时。该应用程序不会关闭 sheet 视图。它只会将视图弹出到父视图。就像下面的 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")
}
)
}
}
这个功能是怎么实现的? :) 请帮助我,谢谢。 :)
我从来没有尝试过 SwiftUI,但我来自 UIKit + RxSwift,所以我知道绑定是如何工作的。我从 SwiftUI 教程中阅读了相当多的示例代码,您关闭模态的方式实际上是正确的,但显然不适用于导航堆栈。
我刚才学到的一种方法是使用 @Binding
变量。这可能不是最好的解决方案,但它奏效了!
所以你的 ContentView
中有这个 $isShowSheet
。通过在 NavView
.
NavView
结构
ContentView
.....
}.sheet(isPresented: $isShowSheet) {
NavView(isShowSheet: self.$isShowSheet)
}
NavView
struct NavView: View {
@Binding var isShowSheet: Bool
var body: some View {
NavigationView {
NavigationLink(destination: NavSubView(isShowSheet: self.$isShowSheet)) {
Text("Enter Sub View")
}
} .navigationViewStyle(StackNavigationViewStyle())
}
}
最后,对您的子视图执行相同的操作。
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
瞧!
更新: 恢复原始版本 - 应按预期对主题发起人的代码进行以下更改。测试与 Xcode 13 / iOS 15
由于 sheet 中的导航可能足够长并且不能在所有导航子视图中关闭,我更喜欢使用环境来仅在需要的地方指定关闭功能,而不是通过所有导航传递绑定堆叠。
这是可能的方法(使用 Xcode 11.2 / iOS 13.2 测试)
- 定义存储sheet状态的环境密钥
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 内容的根目录,因此在声明时它将在任何子视图中可用
}.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 代码的改进版本,因为他们不接受我的编辑。主要归功于他们。
由于 sheet 中的导航可能足够长并且不能在所有导航子视图中关闭,我更喜欢使用环境来仅在需要的地方指定关闭功能,而不是通过所有导航传递绑定堆叠.
这是可能的方法(使用 Xcode 13 / iOS 15 测试)
定义存储sheet状态的环境密钥
struct ShowingSheetKey: EnvironmentKey { static let defaultValue: Binding<Bool>? = nil } extension EnvironmentValues { var isShowingSheet: Binding<Bool>? { get { self[ShowingSheetKey.self] } set { self[ShowingSheetKey.self] = newValue } } }
将此环境值设置为 sheet 内容的根目录,因此在声明时它将在任何子视图中可用
@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 } ) } }