英文:
SwiftUI ForEach View does not update when underlying @StateObject dictionary is updated
问题
以下是翻译好的部分:
"Here are the pieces. I'm trying to have the UI button update the data in Data
and have SwiftUI automatically update another sibling View (that is based on the Data
model). The Data
model dict is updated, but the Cell view in ForEach
does not update."
// 主视图 (ContentView)
import SwiftUI
struct ContentView: View {
@StateObject private var data = Data()
@State private var cells: [Int: [Cell]] = [:]
var body: some View {
VStack {
Text("Debug me")
if let items = cells[0] {
ForEach(items) { item in
item
}
}
}
.onAppear {
setup()
}
}
private func setup() {
var newData: [Cell] = []
newData.append(Cell(key: "A", data: data, text: "1"))
newData.append(Cell(key: "B", data: data, text: "2"))
newData.append(Cell(key: "C", data: data, text: "3"))
cells[0] = newData
}
}
// 子视图 (Cell)
import SwiftUI
struct Cell: View, Hashable, Identifiable {
@State private var key = ""
@ObservedObject private var data: Data
@State var text: String
init(key: String, data: Data, text: String) {
self.key = key
self.data = data
self.text = text
}
// Identifiable
var id: String {
return "\(key)\(text)"
}
// Hashable
func hash(into hasher: inout Hasher) {
hasher.combine(key)
hasher.combine(text)
}
// Equatable
static func ==(lhs: Cell, rhs: Cell) -> Bool {
return lhs.key == rhs.key && lhs.text == rhs.text
}
var body: some View {
HStack {
Text(key)
Text(" / ")
Text(text)
Button(action: confirm) {
Label("", systemImage: "checkmark")
}
}
}
private func confirm() {
// 构造的示例
// 这不起作用
data.updateValue(key: "C", value: "updated123")
}
}
// 数据类 (Data)
import SwiftUI
class Data: ObservableObject {
@Published var data: [String:String]
init() {
data = [:]
}
func updateValue(key: String, value: String) {
data[key] = value
objectWillChange.send()
}
}
英文:
Here are the pieces. I'm trying to have the UI button update the data in Data
and have SwiftUI automatically update another sibling View (that is based on the Data
model). The Data
model dict is updated, but the Cell view in ForEach
does not update.
// main view (ContentView)
import SwiftUI
struct ContentView: View {
@StateObject private var data = Data()
@State private var cells: [Int: [Cell]] = [:]
var body: some View {
VStack {
Text("Debug me")
if let items = cells[0] {
ForEach(items) { item in
item
}
}
}
.onAppear {
setup()
}
}
private func setup() {
var newData: [Cell] = []
newData.append(Cell(key: "A", data: data, text: "1"))
newData.append(Cell(key: "B", data: data, text: "2"))
newData.append(Cell(key: "C", data: data, text: "3"))
cells[0] = newData
}
}
// child view (Cell)
import SwiftUI
struct Cell: View, Hashable, Identifiable {
@State private var key = ""
@ObservedObject private var data: Data
@State var text: String
init(key: String, data: Data, text: String) {
self.key = key
self.data = data
self.text = text
}
// Identifiable
var id: String {
return "\(key)\(text)"
}
// Hashable
func hash(into hasher: inout Hasher) {
hasher.combine(key)
hasher.combine(text)
}
// Equatable
static func ==(lhs: Cell, rhs: Cell) -> Bool {
return lhs.key == rhs.key && lhs.text == rhs.text
}
var body: some View {
HStack {
Text(key)
Text(" / ")
Text(text)
Button(action: confirm) {
Label("", systemImage: "checkmark")
}
}
}
private func confirm() {
// contrived example
// this does not work
data.updateValue(key: "C", value: "updated123")
}
}
// data class (Data)
import SwiftUI
class Data: ObservableObject {
@Published var data: [String:String]
init() {
data = [:]
}
func updateValue(key: String, value: String) {
data[key] = value
objectWillChange.send()
}
}
答案1
得分: 0
正如我在上面的注释中所写,Cell
视图不使用观察对象,因此它和其他属性是完全独立的。
以下是我修改后的 Cell
版本,我将 text
改为了计算属性,因为它实际上代表了从观察对象的发布属性中获取的值,给定 key
值。
struct Cell: View, Hashable, Identifiable {
private let key: String
@ObservedObject private var data: DataModel
var text: String { data.data[key] ?? "" }
init(key: String, data: DataModel, text: String) {
self.key = key
data.data[key] = text
self.data = data
}
var body: some View {
HStack {
Text(key)
Text(" / ")
Text(text)
Button(action: { confirm(key) }) {
Label("", systemImage: "checkmark")
}
}
}
private func confirm(_ key: String) {
data.updateValue(key: key, value: "\(Int.random(in: 1...100))")
}
var id: String { "\(key)" }
func hash(into hasher: inout Hasher) { hasher.combine(key) }
static func ==(lhs: Cell, rhs: Cell) -> Bool { lhs.key == rhs.key }
}
请注意,我将 key
改为了常量,因为它是每个单元格的唯一标识符,并且在符合协议时,我还删除了 text
。另外,我将 Data
重命名为 DataModel
。
英文:
As I wrote in the comment above the Cell
view doesn't make use of the observed object so that it and the other properties are completely independent of each other.
Below is my version of Cell
where I have made text
into a computed property since this is actually what it represent, the value you get from the observed objects published property given the key
value.
struct Cell: View, Hashable, Identifiable {
@State private var key = ""
@ObservedObject private var data: DataModel
var text: String { data.data[key] ?? "" }
init(key: String, data: DataModel, text: String) {
self.key = key
data.data[key] = text
self.data = data
}
var body: some View {
HStack {
Text(key)
Text(" / ")
Text(text)
Button(action: { confirm(key) }) {
Label("", systemImage: "checkmark")
}
}
}
private func confirm(_ key: String) {
data.updateValue(key: key, value: "\(Int.random(in: 1...100))")
}
var id: String { "\(key)" }
func hash(into hasher: inout Hasher) { hasher.combine(key) }
static func ==(lhs: Cell, rhs: Cell) -> Bool { lhs.key == rhs.key }
}
Note that I made key
into a constant since it is the unique identifier for each cell and that I also removed text
when conforming to the protocols because key
is unique. And I renamed Data
to DataModel
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论