环境变量未传递给子视图 [英] Environment variable not passed to Subviews

查看:38
本文介绍了环境变量未传递给子视图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

然后,我复制了 persistentContainer &saveContext 来自一个全新的 iOS 项目(启用了 Core Data),以获得 Core Data 功能.

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

class HostingController: WKHostingController{覆盖 var 主体:AnyView {让 managedObjectContext = (WKExtension.shared().delegate as! ExtensionDelegate).persistentContainer.viewContext返回 AnyView(ContentView().environment(\.managedObjectContext, managedObjectContext))}}

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

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

ContentView.swift

NavigationLink(destination: ProjectsView().environment(\.managedObjectContext, managedObjectContext)) {堆栈{图像(系统名称:文件夹.填充")文本(项目")}}

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

错误消息是环境中的上下文未连接到持久存储协调器:<NSManagedObjectContext: 0x804795e0>.

ProjectsView.swift

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

但话说回来,这不可能吧?那么,是什么导致了这里的错误?

解决方案

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

class ExtensionDelegate: NSObject, WKExtensionDelegate {让persistentContainer = NSPersistentContainer(名称:哈哈")func applicationDidFinishLaunching() {PersistentContainer.loadPersistentStores(completionHandler: { (storeDescription, error) in//处理这个(事情})}}

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

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

class HostingController: WKHostingController{私有(设置)变量上下文:NSManagedObjectContext!覆盖 func 唤醒(带上下文上下文:任何?){self.context = (WKExtension.shared().delegate as! ExtensionDelegate).persistentContainer.viewContext}覆盖 var 主体:AnyView {返回 AnyView(ContentView().environment(\.managedObjectContext, context))}}

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

struct ContentView: View {@Environment(\.managedObjectContext) var moc: NSManagedObjectContextvar主体:一些视图{虚拟堆栈{文本("\(moc)")子视图()}}}结构子视图:查看{@Environment(\.managedObjectContext) var moc: NSManagedObjectContextvar主体:一些视图{文本("\(moc)").foregroundColor(.red)}}

您应该看到上面白色和下面红色的 MOC 地址,无需SubView 上调用 .environment.

I want to get started with Core Data & SwiftUI and therefore created a new watchOS project using the latest Xcode 11.1 GM.

Then, I copied both persistentContainer & saveContext from a fresh iOS project (with Core Data enabled), to gain Core Data capabilities.

After that I modified the HostingController to return AnyView and set the variable in the environment.

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

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

Now I can access the context inside the ContentView, but not in its sub views.
But thats not how it is intended to be? As far as I know, all sub views should inherit its environment from its super views, right?

Right now, to access it inside its sub views I simply set the environment variables again, like this:

ContentView.swift

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

Once I remove the .environment() parameter inside ContentView, the App will crash, because there is no context loaded?!

The error message is Context in environment is not connected to a persistent store coordinator: <NSManagedObjectContext: 0x804795e0>.

ProjectsView.swift

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

But again, that can't be right? So, whats causing the error here?

解决方案

I was able to solve this by fixing up HostingController and guaranteeing the CoreData stack was setup before view construction. First, let's make sure the CoreData stack is ready to go. In ExtensionDelegate:

class ExtensionDelegate: NSObject, WKExtensionDelegate {

    let persistentContainer = NSPersistentContainer(name: "Haha")

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

I had trouble when this property was lazy so I set it up explicitly. If you run into timing issues, make loadPersistentStores a synchronous call with a semaphore to debug and then figure out how to delay nib instantiation until the closure is called later.

Next, let's fix HostingController, by making a reference to a constant view context. WKHostingController is an object, not a struct. So now we have:

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))
    }
}

Now, any subviews should have access to the MOC. The following now works for me:

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)
    }
}

You should see the address of the MOC in white above and in red below, without calling .environment on the SubView.

这篇关于环境变量未传递给子视图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆