SwiftUI:关闭模态

SwiftUI: close modal

我知道这个问题以前有人问过也有人回答过。不确定这个 changed/broke 是否在 SwiftUI 的 Beta 4 中,但我似乎无法获得 isPresented 解决方案来消除 sheet.

显示的模态

这是我尝试过的一个简单示例,我认为这会起作用,但是单击 "Close" 没有任何反应,当我检查 self.isPresented?.value 时它是 nil

struct DetailView: View {
    @Environment(\.isPresented) var isPresented: Binding<Bool>?
    var body: some View {
        Button(action: {
            self.isPresented?.value = false
        }) {
            Text("Close")
        }
    }
}

struct ContentView: View {
    @State private var showingModal = false
    var body: some View {
        Button(action: {
            self.showingModal = true
        }) {
            Text("Show detail")
        }.sheet(isPresented: $showingModal) {
            DetailView()
        }
    }
}

根据建议更新,有效。对我来说似乎记的太多了,希望这能得到更新。

struct DetailView: View {
    @Binding var showingModal: Bool
    var body: some View {
        Button(action: {
            self.showingModal = false
        }) {
            Text("Close")
        }
    }
}

struct ContentView: View {
    @State private var showingModal = false
    var body: some View {
        Button(action: {
            self.showingModal = true
        }) {
            Text("Show detail")
        }.sheet(isPresented: $showingModal) {
            DetailView(showingModal: self.$showingModal)
        }
    }
}

在呈现视图中,将 State 布尔值配置为 false,并将绑定传递给 sheet 调用。要展示,请将其设置为 true。但也将绑定传递到第二个视图,以便那里的按钮可以再次将其设置为 false

struct ContentView : View {
    @State var showSheet = false
    var body: some View {
        Button("Show Sheet") {
            self.showSheet.toggle()
        }.sheet(isPresented: self.$showSheet) {
            Modal(isPresented:self.$showSheet)
        }
    }
}

struct Modal : View {
    @Binding var isPresented : Bool
    var body: some View {
        Button("Done", action: {self.isPresented = false})
    }
}

Beta 6

使用 @Environment 中的 presentationMode

struct SomeView: View {
    @Environment(\.presentationMode) var presentationMode

    var body: some View {
        VStack {
            Text("Ohay!")
            Button("Close") {
                self.presentationMode.wrappedValue.dismiss()
            }
        }
    }
}

一个更简洁的解决方案可能是定义一个回调函数:

struct DetailView: View {
    var dismiss: () -> ()

    var body: some View {
        Button(action: dismiss) {
            Text("Close")
        }
    }
}

struct ContentView: View {
    @State private var showingModal = false

    var body: some View {
        Button(action: {
            self.showingModal = true
        }) {
            Text("Show detail")
        }.sheet(isPresented: $showingModal) {
            DetailView(dismiss: { self.showingModal = false })
        }
    }
}

与额外簿记相比的好处是 DetailView 不再需要知道它是一个模态,允许它在不同的上下文中使用。此外,您保留了所有用于在原始视图中显示和关闭模态的相关代码。

现在,无论您使用哪种方法关闭模态,您都应该警惕模态仍然存在很多问题,即使是在 beta 6 中也是如此。我在各种情况下浪费了太多时间,其中 none解决方案正常工作:

  • 当你把 ContentView 中的 Button 放在 List 中(或 ScrollView,因为 List 只是 [=15 的一种特殊类型=]) 那么这个按钮只能工作 一次。您可以显示模态并关闭它,但您将无法再次显示它...
  • 当您使用 NavigationView 并将 Button 添加到 .navigationBarItems 时,您将能够随心所欲地显示模态。但是,DetailView 中的关闭按钮将不起作用...

到目前为止,我无法使关闭按钮在这些情况下正常工作。这在现实生活中的应用程序中限制了模态的使用。 GM 很有可能会修复此问题,但请注意这些问题,直到已知它们已修复。

另一种解决方案是将 delegate 属性 添加到您的 SwifUI 视图,它将关闭操作传递回演示者。

protocol MySwiftUIViewDelegate: class {
    func myDismissAction()
}

struct MySwiftUIView {
    weak var delegate: MySwiftUIViewDelegate?
    
    var body: some View {
        Button("Dismiss") {
            self.delegate?.myDismissAction()
        }
    }
}

class MyViewController: UIViewController, MySwiftUIViewDelegate {
    func presentMyView() {
        var myView = MySwiftUIView()
        myView.delegate = self

        let hostingViewController = UIHostingController(rootView: myView)
        present(vc, animated: true, completion: nil)
    }

    // MARK: - MySwiftUIViewDelegate

    func myDismissAction() {
        dismiss(animated: true)
    }
}

虽然这看起来有点令人费解,但也可以说让演示者负责处理解雇会更好,这样视图就不必知道它是如何呈现的(例如推送与模态),因此使您的代码更加模块化。另外,您可能需要其他委托方法,具体取决于您正在处理的内容,因此您可能已经有了委托协议。当视图被关闭时,它为您提供了一个方便的地方来执行任何附加代码。

(请记住,根据模式呈现样式/设置,用户也可以通过下拉视图来关闭。)