我正在学习SwiftUI并修改了一个现有示例,因为我想测试是否可以在视图之间访问ObservableObject。阅读文档时,我发现我应该为此使用@EnvironmentObject。我试过了,但是这不起作用:数组人被新人填充(使用调试器),但是UI没有更新。如果我在@EnvironmentObject之外使用people属性,它将按预期工作。
我的代码:
import SwiftUI
struct ContentView: View {
@EnvironmentObject var envTest: EnvTest
var body: some View {
VStack {
ForEach(envTest.people.persons) { person in
Text("\(person.name)")
}
Button(action: {
self.envTest.people.persons.append(Person(id: 4, name: "I am new here"))
}) {
Text("Add/Change name")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static let envTest = EnvTest()
static var previews: some View {
ContentView().environmentObject(envTest)
}
}
class Person: ObservableObject, Identifiable {
var id: Int
@Published var name: String
init(id: Int, name: String) {
self.id = id
self.name = name
}
}
class People: ObservableObject {
@Published var persons: [Person]
init() {
self.persons = [
Person(id: 1, name: "Jabba"),
Person(id: 2, name: "Polke"),
Person(id: 3, name: "Lori")]
}
}
class EnvTest: ObservableObject {
@ObservedObject var people = People()
}
我使用以下命令将EnvTest添加到SceneDelegate:
var window: UIWindow?
var envTest = EnvTest()
func scene(...) {
let contentView = ContentView().environment(\.managedObjectContext, context).environmentObject(envTest)
}
答案 0 :(得分:1)
要获得所需的结果,有两个问题需要解决:
Combine
并将@ObservedObject
中的EnvTest
替换为@Published
EnvTest.objectWillChange
属性的objectWillChange
中,手动触发people
。这两个更改的原因与ObservableObject
的工作方式有关。 documentation告诉我们
一个ObservableObject合成一个
objectWillChange
发布者,该发布者在其@Published属性中的任何一个发生更改之前就发出更改后的值。
如果您没有@Published
属性,那么People.objectWillChange
发布者将不会发出任何信息。解决此问题后,当我们更改persons
上的People
属性时,就会触发People.objectWillChange
发布者发出一个值。但是,揭示了第二个问题,SwiftUI正在监听EnvTest.objectWillChange
发布者,因此我们不会看到UI更新。这就是为什么当我们从send
发布者那里收到一个值时,我们必须在EnvTest.objectWillChange
发布者上手动调用People.objectWillChange
来通知SwiftUI它需要生成新的视图。
如果将objectWillChange
属性包装器放在符合@Published
的类型的属性上时,如果Combine自动传播的ObservableObject
调用会很好。同样,如果SwiftUI观看最低的ObservableObject
而不是观看最高的People
(而不是观看EnvTest
),那就太好了。也许将来会引入这种方法,因为当前手动链接两者的解决方案很痛苦,而且伸缩性不好。
执行所需代码的完整解决方案是:
import SwiftUI
import Combine
struct ContentView: View {
@EnvironmentObject var envTest: EnvTest
var body: some View {
VStack {
ForEach(envTest.people.persons) { person in
Text("\(person.name)")
}
Button(action: {
self.envTest.people.persons.append(Person(id: 4, name: "I am new here"))
}) {
Text("Add/Change name")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static let envTest = EnvTest()
static var previews: some View {
ContentView().environmentObject(envTest)
}
}
class Person: ObservableObject, Identifiable {
var id: Int
@Published var name: String
init(id: Int, name: String) {
self.id = id
self.name = name
}
}
class People: ObservableObject {
@Published var persons: [Person]
init() {
self.persons = [
Person(id: 1, name: "Jabba"),
Person(id: 2, name: "Polke"),
Person(id: 3, name: "Lori")]
}
}
class EnvTest: ObservableObject {
@Published var people = People()
init(people: People = People()) {
self.people = people
people.objectWillChange.receive(subscriber: Subscribers.Sink(receiveCompletion: { _ in }) {
self.objectWillChange.send()
})
}
}
答案 1 :(得分:0)
好吧,这似乎是不可能的,但是随着我对Swift的了解越来越好,我知道我可以轻松地在视图之间共享可观察对象,而无需将其嵌入到EnvironmentObject中。