若模型包装在@State中,则SwiftUI列表项不会更新



给定

  • 带有简单列表的视图
  • 列表中每个元素的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

最新更新