通过按钮中的赋值更新 EnvironmentObject 变量

Updating EnvironmentObject Variable by Assignment in Button

我正在使用一个一次性项目来尝试熟悉 SwiftUI。本质上,我有各种类型的苹果,我通过 EnvironmentObject 变量提供了这些苹果。该项目与我完成的 Landmarks 教程类似,但我正在扩展步进器和按钮等对象的使用。

我目前正在尝试有一个按钮,当按下时,保存某种苹果的 UUID 并将其发送回原始视图。它不起作用,我不确定为什么。这似乎是 environmentObject 赋值没有逃避 action: 闭包的问题。已设置打印语句和文本视图以显示特定点的变量值。虽然它似乎在闭包中设置了变量,但它并没有逃脱闭包并且变量从未真正更新过。

func scene(
  _ scene: UIScene,
  willConnectTo session: UISceneSession,
  options connectionOptions: UIScene.ConnectionOptions
) {
  if let windowScene = scene as? UIWindowScene {
    let window = UIWindow(windowScene: windowScene)
    window.rootViewController = UIHostingController(rootView: ContentView().environmentObject(UserData()))
    self.window = window
    window.makeKeyAndVisible()
  }
}

struct AppleData: Codable, Hashable, Identifiable {
  let id: UUID
  var appleType: String
  var numberOfBaskets: Int
  var numberOfApplesPerBasket: [Int]
  var fresh: Bool

  static let `default` = Self(id: UUID(uuidString: "71190FD1-C8E0-4A65-996E-9CE84D200FBA")!,
                              appleType: "appleType",
                              numberOfBaskets: 1,
                              numberOfApplesPerBasket: [0],
                              fresh: true)  // for purposes of automatic preview

  func image(forSize size: Int) -> Image {
    ImageStore.shared.image(name: appleType, size: size)
  }
}

let appleData: [AppleData] = load("apples.json")
var appleUUID: UUID?

func load<T: Decodable>(_ filename: String, as type: T.Type = T.self) -> T {
  ... // Code Omitted For Brevity
}

final class UserData: ObservableObject {
  let willChange = PassthroughSubject<UserData, Never>()

  var apples = appleData {
    didSet {
      willChange.send(self)
    }
  }
  var appleId = appleUUID {
    didSet {
      willChange.send(self)
    }
  }
}

struct ContentView : View {
  @EnvironmentObject private var userData: UserData

  var body: some View {
  NavigationView {
    List {
      ForEach(appleData) { apple in
        NavigationLink(
          destination: AppleDetailHost(apple: apple).environmentObject(self.userData)
        ) {
          Text(verbatim: apple.appleType)
        }
      }
      Text("self.userData.appleId:  \(self.userData.appleId?.uuidString ?? "Nil")")
    }
    ... // Code Omitted For Brevity
  }
}

struct AppleDetail : View {
  @EnvironmentObject var userData: UserData
  @State private var basketIndex: Int = 0

  var apple: AppleData

  var totalApples: Int {
    apple.numberOfApplesPerBasket.reduce(0, +)
  }

  var body: some View {
    VStack {
      ... // Code Omitted For Brevity
    }

    Button(action: {
      print("self.userData.appleId: \(self.userData.appleId?.uuidString ?? "Nil")")
      self.userData.appleId = self.apple.id
      print("self.userData.appleId: \(self.userData.appleId?.uuidString ?? "Nil")")
    }) {
      Text("Use Apple")
    }
    Text("self.apple.id: \(self.apple.id.uuidString)")
    Text("self.userData.appleId: \(self.userData.appleId?.uuidString ?? "Nil")")
  }
  ... // Code Omitted For Brevity
}

Button in AppleDetail 中打印语句的输出是:

self.userData.appleId: Nil self.userData.appleId: 28EE7739-5E5A-4CA4-AFF5-7A6BFE025250

ContentView中显示self.userData.appleIdText视图总是Nil。任何帮助将不胜感激。

在 beta 5 中,ObservableObject 不再使用 willChange。它使用 objectWillChange 代替。此外,它还会自动合成主题,因此您不必自己编写(尽管您可以根据需要覆盖它)。

除此之外,还有一个新的 属性 包装器 (@Published),它将对 属性 进行更改以让发布者发出。无需手动调用 .send(),因为它会自动完成。因此,如果在您的代码中,您像这样重写 UserData class,它将正常工作:

final class UserData: ObservableObject {
    @Published var apples = appleData
    @Published var appleId = appleUUID
}