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 模式表在关闭后重新打开

SwiftUI - 动画过渡

SwiftUI:.sheet() 在关闭当前工作表时不会使用预期数据转到上一个视图

从 NavigationView 呈现的 SwiftUI 关闭模式表(Xcode Beta 5)

SwiftUI - 如何关闭假的模态视图 - 从里面的第二个视图,用关闭按钮?

如何在 swiftUI 视图中使用按钮? [关闭]