Setting a SwiftUI @EnvironmentObject from outside a view

假如想象 提交于 2021-02-16 13:58:12

问题


I'd like to have a worker task update a SwiftUI view.

The worker task is busy doing the procedural work of the application - playing sounds, and firing timer-based events. I'd like to to flash several icons in a SwiftUI view during those timer events. So I want to trigger a view refresh in those icon views.

So, I created an environmentObject called Settings. It's instantiated in the App Delegate, and attached to the root view in the SceneDelegate.

The Settings object works just fine inside the SwiftUI View hierarchy.

The problem is the dreaded:

Fatal error: No ObservableObject of type Settings found. A View.environmentObject(_:) for Settings may be missing as an ancestor of this view.

I think that the problem is that the worker class is instantiated in the AppDelegate, and Settings is not yet an ObservableObject yet when it's instantiated. But I'm confused.

The environment object is straightforward:

import SwiftUI
import Combine

final class Settings: ObservableObject {
   @Published var showMenu: Bool = true
   @Published var lessonNum: Int = 0

   @Published var arrowsOn: Bool = false {
       willSet {
           willChange.send(self)
       }
   }
}

let willChange = PassthroughSubject<Settings, Never>()

It's instantiated, along with the worker class, in the AppDelegate:

let settings = Settings()
...
var workerClass  = WorkerClass()
var leftArrow = LeftArrowView()

And it's passed to the SceneDelegate:

            window.rootViewController = UIHostingController(rootView: contentView
                .environmentObject(settings)

The sub view that uses settings looks at the environment object to draw the icon in either the on or off state:

import SwiftUI

struct LeftArrowView: View {
@EnvironmentObject var settings: Settings

    let leftArrowOnImage = Image("Arrow Left On").renderingMode(.original)
    let leftArrowOffImage = Image("Arrow Left Off").renderingMode(.original)

    var body: some View {
        ZStack {
            if settings.arrowsOn {
                leftArrowOnImage
            } else {
                leftArrowOffImage
            }
        }
    }
}

The worker class is called as a Button action from higher up in the SwiftUI view hierarchy.

Inside the worker class I attempt to attach to the settings environment object:

import Combine

public class WorkerClass : NSObject, ObservableObject {

    @EnvironmentObject var settings: Settings

and inside a method that's invoked via a timer, I attempt to update a variable in the environment object:

       settings.arrowsOn = !settings.arrowsOn
        print("Arrows are \(settings.arrowsOn)")

... which is when I discover that I failed to actually attach properly to the environment object.

What did I miss?

Thanks in advance for any insights...


回答1:


The @EnvironmentObject wrapper is only to be used inside SwiftUI view, in other places you can use reference types in regular way, so here is possible solution

public class WorkerClass : NSObject, ObservableObject {
    var settings: Settings    // reference property

and when create

let settings = Settings()
...
var workerClass  = WorkerClass(settings: settings) // << same settings as in view


来源:https://stackoverflow.com/questions/62268309/setting-a-swiftui-environmentobject-from-outside-a-view

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!