关闭工作表 SwiftUI

Posted

技术标签:

【中文标题】关闭工作表 SwiftUI【英文标题】:Dismiss sheet SwiftUI 【发布时间】:2019-07-27 14:55:19 【问题描述】:

我正在尝试为我的模式表实现一个关闭按钮,如下所示:

struct TestView: View 
    @Environment(\.isPresented) var present
    var body: some View 
        Button("return") 
            self.present?.value = false
        
    


struct DataTest : View 
    @State var showModal: Bool = false

    var modal: some View 
        TestView()
    
    var body: some View 
        Button("Present") 
            self.showModal = true
        .sheet(isPresented: $showModal) 
            self.modal
        
    

但是点击返回按钮时什么都不做。当模态显示时,控制台中会出现以下内容:

[WindowServer] display_timer_callback:意外状态(现在:5fbd2efe5da4

如果你强制解开present,你会发现它是nil

如何以编程方式关闭.sheet

【问题讨论】:

ios 15 开始,我们可以使用 DismissAction - 请参阅 this answer。 【参考方案1】:

使用@Environment 中的presentationMode

测试版 6

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

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

【讨论】:

不工作。您能否解释更多或添加更多代码。 关闭工作表的两种方法(这个方法和注入 isShowing 绑定)的一个令人讨厌的事情是,当您点击关闭时,工作表方法的 onDismiss 闭包不会触发按钮。但是如果用户通过向下滑动来关闭模式,它将触发。可能会向 Apple 提出这个问题。 不幸的是,这似乎不适用于 macOS 导航视图,其中列表和视图同时显示。有什么已知的方法吗? 由于presentationMode 是一个绑定路径,您实际上可以在@Environment 中嵌套一个@Binding 以摆脱.wrappedValue 部分。 在工作表中使用 NavigationView 时,此选项不起作用。 dismiss() 弹出导航视图。它不会关闭工作表。【参考方案2】:

对我来说,beta 4 打破了这种方法——使用环境变量isPresented——使用关闭按钮。这就是我现在所做的:

struct ContentView: View 
    @State var showingModal = false

    var body: some View 
        Button(action: 
           self.showingModal.toggle()
        ) 
           Text("Show Modal")
        
        .sheet(
            isPresented: $showingModal,
            content:  ModalPopup(showingModal: self.$showingModal) 
        )
    

在你的模态视图中:

struct ModalPopup : View 
    @Binding var showingModal:Bool

    var body: some View 
        Button(action: 
            self.showingModal = false
        ) 
            Text("Dismiss").frame(height: 60)
        
    

【讨论】:

是的,这是目前唯一的方法。 isPresented 仅在您将其与已弃用的 PresentationLink 一起使用时才有效,但前提是它不用作导航栏项目。 @Marc_T.,我之前在这里提到过一次,回复和我一样沮丧——为什么要改变? (我显然不是 Swift Evolution 的一部分 - 希望我说得对。) Beta 2 == PresentationButton。测试版 3 == PresentationLink。 Beta 4 == 嗯,对不起,这一切都已被弃用并且“你搞砸了”?您是否有关于为什么会发生这种事情的链接?相信我,我了解测试版。理解我们也在谈论版本一,如Swift 1.0。也许我对这种事情背后的政治有点天真——但我很聪明。并且很想知道发生了什么。【参考方案3】:

iOS 15

从 iOS 15 开始,我们可以使用 DismissAction,它可以作为 @Environment(\.dismiss) 访问。

不再需要使用presentationMode.wrappedValue.dismiss()

struct SheetView: View 
    @Environment(\.dismiss) var dismiss

    var body: some View 
        NavigationView 
            Text("Sheet")
                .toolbar 
                    Button("Done") 
                        dismiss()
                    
                
        
    

【讨论】:

【参考方案4】:

Apple 推荐(在WWDC 2020 Data Essentials in SwiftUI 中)为此使用@State@Binding。他们还将 isEditorPresented 布尔值和工作表的数据放在使用 @State 声明的同一 EditorConfig 结构中,以便可以对其进行变异,如下所示:

import SwiftUI

struct Item: Identifiable 
    let id = UUID()
    let title: String


struct EditorConfig 
    var isEditorPresented = false
    var title = ""
    var needsSave = false
    
    mutating func present() 
        isEditorPresented = true
        title = ""
        needsSave = false
    
    
    mutating func dismiss(save: Bool = false) 
        isEditorPresented = false
        needsSave = save
    


struct ContentView: View 
    
    @State var items = [Item]()
    @State private var editorConfig = EditorConfig()
    
    var body: some View 
        NavigationView 
            Form 
                ForEach(items)  item in
                    Text(item.title)
                
            
            .navigationTitle("Items")
            .toolbar 
                ToolbarItem(placement: .primaryAction) 
                    Button(action: presentEditor) 
                        Label("Add Item", systemImage: "plus")
                    
                
            
            .sheet(isPresented: $editorConfig.isEditorPresented, onDismiss: 
                if(editorConfig.needsSave) 
                    items.append(Item(title: editorConfig.title))
                
            ) 
                EditorView(editorConfig: $editorConfig)
            
        
    
    
    func presentEditor() 
        editorConfig.present()
    


struct EditorView: View 
    @Binding var editorConfig: EditorConfig
    var body: some View 
        NavigationView 
            Form 
                TextField("Title", text:$editorConfig.title)
            
            .toolbar 
                ToolbarItem(placement: .confirmationAction) 
                    Button(action: save) 
                        Text("Save")
                    
                    .disabled(editorConfig.title.count == 0)
                
                ToolbarItem(placement: .cancellationAction) 
                    Button(action: dismiss) 
                        Text("Dismiss")
                    
                
            
        
    
    
    func save() 
        editorConfig.dismiss(save: true)
    
    
    func dismiss() 
        editorConfig.dismiss()
    


struct ContentView_Previews: PreviewProvider 
    static var previews: some View 
        ContentView(items: [Item(title: "Banana"), Item(title: "Orange")])
    

【讨论】:

以上是关于关闭工作表 SwiftUI的主要内容,如果未能解决你的问题,请参考以下文章

关闭工作表 SwiftUI

SwiftUI:以编程方式关闭工作表

SwiftUI 工作表在第一次呈现时被关闭

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

SwiftUI - 如何防止工作表中的键盘向上推我的主 UI

SwiftUI 模式表在关闭后重新打开