【问题标题】:SwiftUI: Cannot dismiss sheet after creating CoreData objectSwiftUI:创建 CoreData 对象后无法关闭工作表
【发布时间】:2021-01-12 20:02:36
【问题描述】:

我正在尝试创建一个具有两个视图的非常基本的应用程序。主视图在 NavigationView 中显示 CoreData 对象,并具有用于打开“添加项目”模式表的工具栏按钮。 “添加项目”表应该能够插入一个新的 CoreData 对象并自行关闭。我观察到的是,只要模式表不插入 CoreData 对象,它就可以被关闭,但是一旦它与 CoreData 交互,我所有试图关闭工作表的尝试都不再有效。此外,当工作表创建 CoreData 对象时,我收到以下错误:

[演示]尝试本 TtGC7SwiftUI22SheetHostingControllerVS_7AnyView EM>:0x7f8fcbc49a10>上 TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier EM> _:0x7f8fcbd072c0>(来自 TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVVS_22_VariadicView_Children7ElementGVS_18StyleContextWriterVS_19SidebarStyleContext EM> __:0x7f8fbbd0ba80> ) 这已经在呈现 TtGC7SwiftUI22SheetHostingControllerVS_7AnyView: 0x7f8fcbd310b0>。

我使用的大部分代码都是 Xcode 自己的样板代码,以及从网络上借来的常见模式。你能帮我调试一下吗?

为了重现,在 Xcode 12 中,使用 CoreData 创建一个新的 SwiftUI iOS 应用程序。将 ContentView.swift 替换为:

import SwiftUI
import CoreData

struct ContentView: View {
    @Environment(\.managedObjectContext) private var viewContext

    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)],
        animation: .default)
    private var items: FetchedResults<Item>
    
    @State var showingSheet: Bool = false

    var body: some View {
        NavigationView {
            List {
                //Text("static text")

                ForEach(items) { item in
                    Text("Item at \(item.timestamp!, formatter: itemFormatter)")
                }
                .onDelete(perform: deleteItems)
            }
            .navigationTitle("List of items")
            .toolbar {
                ToolbarItem(placement: .primaryAction) {
                    Button(action: {
                        showingSheet = true
                    }) {
                        Text("Open sheet")
                    }
                    .sheet(isPresented: $showingSheet) {
                        MySheetView(isPresented: $showingSheet)
                            .environment(\.managedObjectContext, viewContext)
                    }
                }
            }
        }
    }

    private func deleteItems(offsets: IndexSet) {
        withAnimation {
            offsets.map { items[$0] }.forEach(viewContext.delete)
            do {
                try viewContext.save()
            } catch {
                let nsError = error as NSError
                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
            }
        }
    }
}

private let itemFormatter: DateFormatter = {
    let formatter = DateFormatter()
    formatter.dateStyle = .short
    formatter.timeStyle = .medium
    return formatter
}()

创建一个新的 MySheetView.swift:

struct MySheetView: View {
    @Environment(\.managedObjectContext) private var viewContext
    @Binding var isPresented: Bool
    
    var body: some View {
        NavigationView {
            Form {
                Button(action: {
                    self.isPresented = false
                }) {
                    Text("Dismiss this sheet")
                }
                
                Button(action: {
                    let newItem = Item(context: viewContext)
                    newItem.timestamp = Date()

                    do {
                        try viewContext.save()
                    } catch {
                        let nsError = error as NSError
                        fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
                    }
                }) {
                    Text("Add Item")
                }
            }
        }
    }
}

运行时,如果你点击“打开工作表”,然后你可以点击“关闭此工作表”,它就可以工作了。但是,如果您点击“打开工作表”,然后点击“添加项目”,则会打印错误并且“关闭此工作表”按钮将停止工作。 (您仍然可以向下滑动以关闭工作表。)

我也尝试了presentationmode.wrappedvalue.dismiss()方法,没有成功。

我发现,如果您在主视图上注释掉 ForEach 循环并取消注释 Text("static text"),那么在工作表上添加项目将不再打印出错误或中断工作表的解除。所以这个问题在某种程度上与在主工作表上显示数据有关。但显然我确实需要实际显示数据,这似乎是一种非常常见和基本的模式。我做错了吗?

【问题讨论】:

    标签: ios swift core-data swiftui


    【解决方案1】:

    您的.sheet 被放置在 ToolbarItem 上。只需在NavigationView 上更改它即可。

    这是 ContentView 的主体

    var body: some View {
        NavigationView {
            List {
                //Text("static text")
    
                ForEach(items, id:\.self) { item in
                    Text("Item at \(item.timestamp!, formatter: itemFormatter)")
                }
                .onDelete(perform: deleteItems)
            }
            .navigationTitle("List of items")
            .toolbar {
                ToolbarItem(placement: .primaryAction) {
                    Button(action: {
                        showingSheet = true
                    }) {
                        Text("Open sheet")
                    }
                }
            }
            .sheet(isPresented: $showingSheet) {
                MySheetView(isPresented: $showingSheet)
                    .environment(\.managedObjectContext, viewContext)
            }
    
        }
    }
    

    【讨论】:

    • 确实如此,非常感谢!我已经坚持了大约 5 个小时的反复试验。 真的希望 Apple 的 .sheet 或 NavigationView 文档中对此进行了解释。令人沮丧的是,它几乎按照我的方式工作。如果它未能显示工作表,我会知道问题出在哪里。
    • 你也一样@gtrichar!谢谢@davidev!
    • 您好,我遇到了同样的问题,当我使用导航链接时,我尝试保存它关闭的 CoreData,然后立即创建一个新视图,很难弄清楚如何修复它。将其更改为按钮并实施此解决方案有效,但我不想要工作表。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-17
    • 2020-07-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-23
    相关资源
    最近更新 更多