SwiftUI:如何使用位置管理器类的已发布属性更新一个类ObservableObject的已发布属性?

问题描述

我想用从 LocationManager 传入的数据更新我的 ChallengeManager 类的 Published 属性。 这是带有相关位的简化代码

位置管理器

final class LocationManager: NSObject,ObservableObject {
    var challengeManager = ChallengeManager()
    ...
    //a func called from locationManager delegate converts the region to an instance of an Area object then calls a method on the ChallengeManager class like this:
    
    challengeManager.loadChallenge(for: activeArea)
...

挑战管理器

final class ChallengeManager: ObservableObject {
   @Published var isShowingChallenge = false
   @Published var challengetodisplay: Challenge?

func loadChallenge(for area: Area) {
   if let challenge = area.challenge { //gets challenge property of area object
      self.challengetodisplay = challenge
      self.isShowingChallenge = true
   }
}

最后是 ContentView:

struct ContentView: View {
   @Observedobject var challengeManager = ChallengeManager()
...

(老实说,我可以通过在 View 中为 LocationManager 添加一个 Observedobject 然后将值传递到那里的 func 来获得我想要的结果。但我不喜欢这样做的想法多个视图。而且我还希望 loadChallenge() 做更多繁重的工作。在我看来,它应该是唯一的事实来源。不是吗?)

问题:

如果我尝试访问 ContentView 内的challengeManager.challengetodisplay,该值始终为零。 loadChallenge() 函数中的打印语句告诉我来自 locationManager 的值被正确接收。但是@Published var challengetodisplay 没有改变。 有人可以告诉我我做错了什么吗?
谢谢!

解决方法

@ObservedObject var challengeManager = ChallengeManager()

这将创建一个新的 ChallengeManager 实例,它不会是您在 LocationManager 中处理的那个实例。

您应该在实例化 ContentView 时传入挑战管理器,而不是给属性一个默认值。

, challengeManager 中的

LocationManagerchallengeManager 中的 ContentView 是两个不同的实例。他们没有关系。您必须使用相同的实例。

约定是

  • @StateObject 创建一个对象并拥有它

    @StateObject var challengeManager = ChallengeManager()
    
  • @ObservedObject 不创建和拥有对象,它将通过视图层次结构传递。

    @ObservedObject var challengeManager : ChallengeManager
    

您也可以在视图层次结构的开头使用 @EnvironmentObject

附注:

SwiftUI 比 Swift 更依赖于非可选项。对于已发布的挑战,如果更像 SwiftUI,则此枚举具有关联类型

enum ChallengeState {
    case idle,display(Challenge)
}

final class ChallengeManager: ObservableObject {
   @Published var challengeState : ChallengeState = .idle

func loadChallenge(for area: Area) {
   if let challenge = area.challenge { //gets challenge property of area object
      challengeState = .display(challenge)
   } else {
      challengeState = .idle
   }
}

在视图 switch 上的状态。