SwiftUI - 如何关闭 sheet 视图,同时解散该视图

15

我想实现这个功能,就像苹果公司的“查找”视图一样。

我的目标是当表视图通过导航推出另一个视图时,用户可以点击导航项按钮关闭表视图。 就像下面这个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")
            }
        )
    }
}

我是怎么实现这个功能的? :) 请帮忙,谢谢。 :)

3个回答

26

更新: 恢复原始版本 - 提供以下更改,应根据主题发起者的代码意图进行。 经过测试,已在Xcode 13 / iOS 15上工作。

由于在sheet中导航可能足够长,并且关闭可能不在所有导航子视图中,因此我更喜欢使用环境来有能力仅在需要的地方指定关闭功能,而不是通过所有导航堆栈传递绑定。

这里是一种可能的方法(经过Xcode 11.2 / iOS 13.2测试)

  1. 定义环境密钥以存储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(isPresented: $isShowSheet) {
    NavView()
       .environment(\.showingSheet, self.$isShowSheet)
}
  1. 仅在将要使用环境值的子视图中声明和使用它
struct NavSubView: View {
    @Environment(\.showingSheet) var showingSheet

    var body: some View {
        Text("Hello")
        .navigationBarItems(trailing:
            Button("Done") {
                self.showingSheet?.wrappedValue = false
            }
        )
    }
}

Asperi,你的代码不起作用,也无法编译。我已经修复了它(在我的第二次编辑中)。 - LinusGeffarth
我不知道为什么你不接受我的编辑 - 它添加了有价值的额外代码(之前缺失的!)并修复了拼写错误/改进了英语,从而使其更易于理解和 less 混淆。 - LinusGeffarth

9

我之前从事UIKit+RxSwift,虽然没有尝试过SwiftUI,但我对绑定有一定的了解。我在SwiftUI教程中阅读了很多示例代码,发现关闭模态框的方式是正确的,但对于导航栏不起作用。

刚刚学到的一种方法是使用@Binding变量。这可能不是最好的解决方案,但它有效!

因此,你在ContentView中有$isShowSheet对象。通过声明一个变量在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())
    }
}

最后,对你的子视图做同样的事情。
导航子视图。
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

大功告成!

enter image description here


-2
这是一个改进版的Asperi之前的代码,因为他们不接受我的编辑。主要的功劳归功于他们。

由于表格中的导航可能足够长,并且关闭不一定在所有导航子视图中,因此我更喜欢使用环境来指定仅在需要的位置指定关闭功能,而不是通过整个导航堆栈传递绑定。

这里是可能的方法(已在Xcode 13 / iOS 15上进行了测试)

  1. 定义环境键以存储表格状态

     struct ShowingSheetKey: EnvironmentKey {
         static let defaultValue: Binding<Bool>? = nil
     }
    
     extension EnvironmentValues {
         var isShowingSheet: Binding<Bool>? {
             get { self[ShowingSheetKey.self] }
             set { self[ShowingSheetKey.self] = newValue }
         }
     }
    
  2. 将此环境值设置为表格内容的根,这样在声明时它将在任何子视图中可用

     @State var isShowingSheet = false
    
     ...
    
     Button("打开表格") {
         isShowingSheet?.wrappedValue = true
     }
     // 注意isShowingSheet前面没有$
     .sheet(isPresented: isShowingSheet ?? .constant(false)) {
         NavView()
            .environment(\.isShowingSheet, self.$isShowingSheet)
     }
    
  3. 仅在将使用环境值的子视图中声明和使用

     struct NavSubView: View {
         @Environment(\.isShowingSheet) var isShowingSheet
    
         var body: some View {
             Text("你好")
             .navigationBarItems(trailing:
                 Button("完成") {
                     isShowingSheet?.wrappedValue = false
                 }
             )
         }
     }
    

这段代码无法工作,状态不是绑定的,请您检查一下? - Bruno
你遇到了什么问题?看到错误信息了吗?我这边正常运行。@Bruno - LinusGeffarth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接