当使用ForEach更改数据时,视图不更新



我想改变速率参数并在ForEach中显示。

// ViewModel.swift
@MainActor final class ViewModel: ObservableObject {
let serviceContainer: ServiceContainerProtocol
@Published var funds: [FundModel] = []
...
init(serviceContainer: ServiceContainerProtocol) {
self.serviceContainer = serviceContainer
Task { await getFunds() }
...
}
...
}
// FundView.swift
struct FundView: View {
@StateObject private var viewModel: ViewModel
init(serviceContainer: ServiceContainerProtocol) {
self._viewModel = StateObject(wrappedValue: ViewModel(serviceContainer: serviceContainer))
}
var body: some View {
ScrollView { 
VStack {
ForEach(viewModel.funds, id: .fundCode) { fund in
VStack {
Text(String(fund.rate))
Button("Add 5") {
if let index = viewModel.funds.firstIndex(where: { $0.fundCode == fund.fundCode }) {
viewModel.funds[index].rate += 5
}
}
}
}
}
}
}
}

如果我使用Struct模型,视图会按预期更新。

// FundModel.swift
struct FundModel: Decodable {
let fundCode: String
...
// Internal - Not related to api.
var rate: Int = 0
...
// MARK: CodingKeys
private enum CodingKeys: String, CodingKey {
case fundCode
...
}
// MARK: Decodable
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
fundCode = try container.decode(String.self, forKey: .fundCode)
...
}
}

如果我使用Class作为模型,视图没有更新。

// FundModel.swift
final class FundModel: Decodable {
let fundCode: String
...
// Internal - Not related to api.
var rate: Int = 0
...
// MARK: CodingKeys
private enum CodingKeys: String, CodingKey {
case fundCode
...
}
// MARK: Decodable
public required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
fundCode = try container.decode(String.self, forKey: .fundCode)
...
}
}

我想用Class作为模型,因为它需要从某个超类继承一些属性。

SwiftUI透视图的结构模型和类模型之间的区别是什么,为什么当我使用类模型超过结构模型时视图没有更新?

注意:FundModel在其他地方符合Equtable和Hashable。

使用class ViewModel: ObservableObject {...}(不含@MainActor)和viewModel.objectWillChange.send()尝试此方法如下例代码所示:

struct FundView: View {
@StateObject private var viewModel: ViewModel
init(serviceContainer: ServiceContainerProtocol) {
self._viewModel = StateObject(wrappedValue: ViewModel(serviceContainer: serviceContainer))
}
var body: some View {
ScrollView {
VStack {
ForEach(viewModel.funds, id: .fundCode) { fund in
VStack {
Text(String(fund.rate))
Button("Add 5") {
if let index = viewModel.funds.firstIndex(where: { $0.fundCode == fund.fundCode }) {
viewModel.objectWillChange.send() // <-- here
viewModel.funds[index].rate += 5
}
}
}
}
}
}
}
}
final class FundModel: Decodable {
let fundCode: String
var rate: Int = 0
//....
}

@Published var变化时视图更新。你的@Published varFundModel的数组。

struct不是一个可变的实体,所以当你使用struct时,数组实际上用一个新的对象替换了这个对象。系统识别数组中的变化并将其发布到视图。

类是可变的,所以当你在类中改变速率时,你仍然有相同的实例。因此,FundModel的数组没有改变:它仍然包含完全相同的元素,即使其中一个元素改变了内部变量。

@Published包装器不能检测到数组成员的内部变量发生了变化:它只检测到数组中有不同的元素。

结论:struct需要创建一个新的元素来改变,@Published认识到这一点。class可以改变,所以对于@Published对象仍然是相同的(数组没有改变)。

最新更新