ObservedObject的修改子视图未更新



我正在尝试构建一个View,它是一个对象的子列表,我希望这些子列表是可修改的。数据改变了,但是视图没有反应。

下面是简化后的示例。

class ObjectOne: ObservableObject {

@Published var children: [ObjectTwo]

init() {
self.children = []
}
}
class ObjectTwo: ObservableObject {

@Published var value: Value
var id = UUID()

init(value: Value) {
self.value = value
}
}
struct ContentView: View {

@ObservedObject var object: ObjectOne = ObjectOne()

var body: some View {
VStack {
Button {
self.object.children.append(ObjectTwo(value: .one))
} label: {
Text("Add Object")
}
Text("Objects:")
ForEach(self.object.children, id: .id) { object in
HStack {
Text(String(object.value.rawValue))
Spacer()
Button {
if object.value == .one {
object.value = .two
} else {
object.value = .one
}
} label: {
Text("Toggle")
}

}
}
}
}
}

添加对象工作,列表更新,但修改ObjectTwovalue不会更新视图。

当您嵌套ObservableObject时,您需要手动告诉它们即将发生的更改。试试这个,对我有用。

Button {
self.object.objectWillChange.send()  // <--- here
if object.value == .one {
object.value = .two
} else {
object.value = .one
}
} label: {
Text("Toggle")
}

我建议你用struct代替ObjectTwo,这更容易使用(只需要一些小的代码更改),也是我的常用做法。

例如,你可以这样写:

struct ObjectTwo: Identifiable {  // <-- here
var value: Value
var id = UUID()

init(value: Value) {
self.value = value
}
}
struct ContentView: View {
@StateObject var objectModel = ObjectOne() // <-- here

var body: some View {
VStack {
Button {
objectModel.children.append(ObjectTwo(value: .one))
} label: {
Text("Add Object")
}
Text("Objects:")
ForEach($objectModel.children, id: .id) { $object in  // <-- here
HStack {
Text(String(object.value.rawValue))
Spacer()
Button {
if object.value == .one {
object.value = .two
} else {
object.value = .one
}
} label: {
Text("Toggle")
}
}
}
}
}
}

Swift被设计成使用值类型而不是对象(为了防止一致性错误),参见在结构和类之间进行选择- Apple Developer。SwiftUI在实现依赖和变更跟踪时利用了这些值语义,如果你使用对象,你将失去这些特性。一般来说,在SwiftUI中,我们可以使用一个存储对象(用于持久化/同步模型数据)并使用模型结构体,这样我们就可以利用SwiftUI对值类型的依赖跟踪和写访问的@Binding,例如

class Store: ObservableObject {
static var shared = Store()
static var preview = Store(preview: true)
@Published var items: [Item] = []

init(preview: Bool) {
if (preview) {
items = [Item(text: "Test")]
}
else {
load()
}
}
func load(){}
func save(){}
}
class Item: Identifiable {
let id = UUID()
@Published title = ""

init(title: String) {
self.title = title
}
}
@main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(Store.shared)
}
}
}
struct ContentView: View {
@EnvironmentObject var store: Store
var body: View {
List($store.items) { $item in
TextField("Title", text: $item.title)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environmentObject(Store.preview)
}
}

最新更新