首页 > 解决方案 > 环境变量未传递给子视图

问题描述

我想开始使用 Core Data 和 SwiftUI,因此使用最新的 Xcode 11.1 GM 创建了一个新的 watchOS 项目。

然后,我从一个新的 iOS 项目(启用了 Core Data)中复制了两者persistentContainersaveContext以获得 Core Data 功能。

之后我修改了HostingController返回AnyView并在环境中设置变量。

class HostingController: WKHostingController<AnyView> {
    override var body: AnyView {
        
        let managedObjectContext = (WKExtension.shared().delegate as! ExtensionDelegate).persistentContainer.viewContext
        

        return AnyView(ContentView().environment(\.managedObjectContext, managedObjectContext))
    }
}

现在我可以访问 中的上下文ContentView但不能访问其子视图中的上下文。
但这不是它的本意?据我所知,所有子视图都应该从其父视图继承其环境,对吧?

现在,要在其子视图中访问它,我只需再次设置环境变量,如下所示:

内容视图.swift

NavigationLink(destination: ProjectsView().environment(\.managedObjectContext, managedObjectContext)) {
    HStack {
        Image(systemName: "folder.fill")
        Text("Projects")
    }
}

一旦我删除了 ContentView 中的 .environment() 参数,应用程序就会崩溃,因为没有加载上下文?!

错误消息是Context in environment is not connected to a persistent store coordinator: <NSManagedObjectContext: 0x804795e0>

ProjectsView.swift

struct ProjectsView: View {
    @Environment(\.managedObjectContext) var managedObjectContext
    [...]
}

但又一次,这不可能吗?那么,是什么导致了这里的错误?

标签: swiftwatchkitswiftui

解决方案


我能够通过修复HostingController并保证在视图构建之前设置 CoreData 堆栈来解决这个问题。首先,让我们确保 CoreData 堆栈已准备就绪。在ExtensionDelegate

class ExtensionDelegate: NSObject, WKExtensionDelegate {

    let persistentContainer = NSPersistentContainer(name: "Haha")

    func applicationDidFinishLaunching() {
        persistentContainer.loadPersistentStores(completionHandler: { (storeDescription, error) in
            // handle this
        })
    }
}

当这个属性出现时我遇到了麻烦,lazy所以我明确地设置了它。如果遇到时间问题,loadPersistentStores请使用信号量进行同步调用以进行调试,然后找出如何延迟 nib 实例化,直到稍后调用闭包。

接下来,让我们HostingController通过引用常量视图上下文来修复 。WKHostingController是一个对象,而不是一个结构。所以现在我们有:

class HostingController: WKHostingController<AnyView> {

    private(set) var context: NSManagedObjectContext!

    override func awake(withContext context: Any?) {
        self.context = (WKExtension.shared().delegate as! ExtensionDelegate).persistentContainer.viewContext
    }

    override var body: AnyView {
        return AnyView(ContentView().environment(\.managedObjectContext, context))
    }
}

现在,任何子视图都应该可以访问 MOC。以下现在对我有用:

struct ContentView: View {

    @Environment(\.managedObjectContext) var moc: NSManagedObjectContext

    var body: some View {
        VStack {
            Text("\(moc)")
            SubView()
        }
    }
}

struct SubView: View {

    @Environment(\.managedObjectContext) var moc: NSManagedObjectContext

    var body: some View {
        Text("\(moc)")
            .foregroundColor(.red)
    }
}

您应该看到 MOC 的地址在上方为白色,下方为红色,而无需调用..environmentSubView


推荐阅读