【问题标题】:Setting a SwiftUI @EnvironmentObject from outside a view从视图外部设置 SwiftUI @EnvironmentObject
【发布时间】:2020-09-27 18:53:04
【问题描述】:

我想让工作任务更新 SwiftUI 视图。

worker 任务正忙于执行应用程序的程序性工作 - 播放声音和触发基于计时器的事件。我想在这些计时器事件期间在 SwiftUI 视图中闪烁几个图标。所以我想在这些图标视图中触发视图刷新。

所以,我创建了一个名为 Settings 的 environmentObject。它在 App Delegate 中实例化,并附加到 SceneDelegate 中的根视图。

Settings 对象在 SwiftUI 视图层次结构中工作得很好。

问题是可怕的:

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

我认为问题在于工作类是在 AppDelegate 中实例化的,而 Settings 在实例化时还不是 ObservableObject。但我很困惑。

环境对象很简单:

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

它与工作类一起在 AppDelegate 中被实例化:

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

并将其传递给 SceneDelegate:

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

使用设置的子视图查看环境对象以在开启或关闭状态下绘制图标:

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

worker 类在 SwiftUI 视图层次结构中作为 Button 操作从更高层调用。

在工作类中,我尝试附加到设置环境对象:

import Combine

public class WorkerClass : NSObject, ObservableObject {

    @EnvironmentObject var settings: Settings

在通过计时器调用的方法中,我尝试更新环境对象中的变量:

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

...当我发现我实际上未能正确附加到环境对象时。

我错过了什么?

提前感谢您提供任何见解...

【问题讨论】:

    标签: ios swiftui observable combine


    【解决方案1】:

    @EnvironmentObject 包装器只能在 SwiftUI 视图中使用,在其他地方您可以按常规方式使用引用类型,所以这里是可能的解决方案

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

    以及何时创建

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

    【讨论】:

    • 谢谢!我当然可以这样做,但更新设置对象的目的是让 SwiftUI 视图更新。现在我可能完全错了,但我知道包装器的目的是让 Combine 发布对 SwiftUI 订阅的变量的更改。这有意义吗?
    • @Andromeda,包装器的目的是观察发布的值和更新视图,这就是为什么需要它仅在视图中。并且发布的属性将在他们修改的任何时间(以及从何处)发布,这就是为什么在您的 WorkerClass 案例中引用它就足够了。
    • 啊,这就是我没有读完结合书的结果——谢谢!当我这样做时,我会收到一个有用的警告,不要更新后台线程上的已发布变量(我不是)......但仍然没有触发 SwiftUI 视图以进行视图更新。知道可能缺少什么吗?
    • 非常感谢 - 我绝对欠你一杯美味的饮料!
    猜你喜欢
    • 1970-01-01
    • 2020-03-06
    • 1970-01-01
    • 2020-03-28
    • 2020-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多