我想改变速率参数并在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 var
是FundModel
的数组。
struct不是一个可变的实体,所以当你使用struct
时,数组实际上用一个新的对象替换了这个对象。系统识别数组中的变化并将其发布到视图。
类是可变的,所以当你在类中改变速率时,你仍然有相同的实例。因此,FundModel
的数组没有改变:它仍然包含完全相同的元素,即使其中一个元素改变了内部变量。
@Published
包装器不能检测到数组成员的内部变量发生了变化:它只检测到数组中有不同的元素。
结论:struct
需要创建一个新的元素来改变,@Published
认识到这一点。class
可以改变,所以对于@Published
对象仍然是相同的(数组没有改变)。