给定
- 带有简单列表的视图
- 列表中每个元素的ItemView
- 应用程序的模型
- 模型值(Deck(
点击主视图中的按钮,预计模型会发生更改,并将更改传播到ItemView。
问题是,只有当模型结构作为普通变量存储在ItemView中时,更改才会传播;但是如果我添加CCD_ 1属性包装器,则不会发生这些情况。视图将更新但不会更改(就像数据已缓存一样(。
问题1:这是预期行为吗?如果是,为什么?我希望ItemView只在模型更改时更新,方法是观察它抛出@State,这样,无论列表何时命令它,视图都会刷新,即使数据没有更新?
问题2:列表中的项使用普通结构属性作为模型是否正常?在视图模型中处理数组时,使用可观察类会造成更大的复杂性,也会使列表刷新/识别机制变得更复杂
在这个例子中,模型不需要@State,因为更改只来自外部,在现实世界中,当视图本身触发更改时,我会需要它吗?
这是一个精简版,用于重现问题(创建一个项目并用以下内容替换ContentView(:
import SwiftUI
struct Deck: Identifiable {
let id: Int
var name: String
init(_ name: String, _ id: Int) {
self.name = name
self.id = id
}
}
struct ItemView: View {
// @State var deck: Deck // DOES NOT WORK !!! <-------------------
let deck: Deck // WORKS (first element is updated)
var body: some View { Text(deck.name) }
}
class Model: ObservableObject {
@Published var decks: [Deck] = getData()
static func getData(changed: Bool = false) -> [Deck] {
let firstElement = changed ? "CHANGED ELEMENT" : "0"
return [Deck(firstElement, 0), Deck("1", 1), Deck("2", 2)]
}
func changeFirst() { self.decks = Self.getData(changed: true) }
}
struct ContentView: View {
@StateObject var model = Model()
var body: some View {
List {
ForEach(model.decks) { deck in
ItemView(deck: deck)
}
Button(action: model.changeFirst) {
Text("Change first item")
}
}
}
}
使用Xcode 13/iPhone13模拟器(iOS 15(测试
问题1是的,这是意料之中的,因为@State
和@Published
是真理的来源。@State
断开与@Published
的连接并进行复制。
问题2如果所有更改都在外部(单向连接(,则在处理值类型时,不需要为子级使用任何类型的包装器。
如果需要双向连接,则在处理struct
/值类型时使用@Binding
。
https://developer.apple.com/wwdc21/10022
https://developer.apple.com/documentation/swiftui/managing-user-interface-state
https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app