如何更改子模型实体更新 SwiftUI
我正在尝试为经典的父母(主尾巴等)模型实现UI。当我直接引用孩子们的观点时,所有这些都可以正常工作,但是当我为孩子们介绍一个视图模型时,在更新孩子时,UI停止更新。我很确定,当我介绍儿童视图模型时,我正在创建儿童struct的副本,这就是为什么UI未更新的原因。我怀疑我应该使用但尚未发现的常见模式。
我正在使用Xcode 13.3,因此,我相信Swift 5.5。
这是我用来尝试解决问题的代码。此代码不使用子视图模型,并且UI已正确更新:
工作代码
模型
import Foundation
struct Parent {
private(set) var children: Array<Child>
private(set) var numberTimes: Int
init(numberOfChildren: Int) {
numberTimes = 0
children = []
for index in 0..<numberOfChildren {
children.append(Child(value: index))
}
toggleRandom()
}
mutating func choose(child: Child) {
if let chosenIndex = children.firstIndex(where: { $0.value == child.value }) {
children[chosenIndex].toggleIsSelected()
}
}
mutating func pressMe() {
numberTimes += 1
}
mutating func toggleRandom() {
children[Int.random(in: 0..<children.count)].toggleIsSelected()
}
}
struct Child: Identifiable {
let id: Int
private(set) var value: Int
private(set) var isSelected = false
init(value: Int) {
self.id = value
self.value = value
}
mutating func toggleIsSelected() {
isSelected.toggle()
}
}
使用原始儿童的
import SwiftUI
class ParentViewModel: ObservableObject {
@Published var parent: Parent
init(numberOfChildren: Int) {
parent = Parent(numberOfChildren: numberOfChildren)
}
var children: Array<Child> {
parent.children
}
var parentText: String {
"'Press Me' pressed \(parent.numberTimes)"
}
func pressMe() {
parent.pressMe()
}
func toggleRandom() {
parent.toggleRandom()
}
}
原始儿童视图的
import SwiftUI
struct ContentView: View {
@ObservedObject var parentViewModel: ParentViewModel
var body: some View {
VStack {
Text(parentViewModel.parentText).padding()
Button { parentViewModel.pressMe() }
label: { Text("Press Me") }
ForEach(parentViewModel.children, id: \.id) { child in
Text(String(child.value))
.foregroundColor(child.isSelected ? .white : .black)
.background(child.isSelected ? .black : .white)
}
Button { parentViewModel.toggleRandom() }
label: { Text("Toggle Random") }
}
}
}
ViewModel这是对儿童数据元素使用视图模型修改的代码。该模型与上面相同,因此只有视图模型和视图代码不同。此代码继续正确地更新父文本,但是当孩子值更改时不会更新UI。
不使用的代码ViewModel
与儿童查看模型
import SwiftUI
class ParentViewModel: ObservableObject {
@Published var parent: Parent
private(set) var children: Array<ChildViewModel>
init(numberOfChildren: Int) {
parent = Parent(numberOfChildren: numberOfChildren)
children = []
for child in parent.children {
children.append(ChildViewModel(child: child))
}
}
var parentText: String {
"'Press Me' pressed \(parent.numberTimes)"
}
func pressMe() {
parent.pressMe()
}
func toggleRandom() {
parent.toggleRandom()
}
}
class ChildViewModel: ObservableObject, Identifiable {
@Published var child: Child
init(child: Child) {
self.child = child
}
var value: Int {
child.value
}
var isSelected: Bool {
child.isSelected
}
}
视图与儿童视图模型
import SwiftUI
struct ContentView: View {
@ObservedObject var parentViewModel: ParentViewModel
var body: some View {
VStack {
Text(parentViewModel.parentText).padding()
Button { parentViewModel.pressMe() }
label: { Text("Press Me") }
ForEach(parentViewModel.children, id: \.id) { child in
ChildView(childViewModel: child)
}
Button { parentViewModel.toggleRandom() }
label: { Text("Toggle Random") }
}
}
}
struct ChildView: View {
@ObservedObject var childViewModel: ChildViewModel
var body: some View {
Text(String(childViewModel.value))
.foregroundColor(childViewModel.isSelected ? .white : .black)
.background(childViewModel.isSelected ? .black : .white)
}
}
I'm trying to implement a UI for a classic parent-children (master-details, etc.) model. All works fine when I have a direct reference to the children in the view, but when I introduce a view-model for the children the UI stops updating when children are updated. I'm pretty sure that when I introduce the child view model I am creating a copy of the child struct and that is why the UI is not being updated. I suspect there is a common pattern I should be using but have not yet discovered.
I'm using Xcode 13.3 and so, I believe, Swift 5.5.
Here's code I'm using to try to solve the problem. This code does not use a child view model and the UI is correctly updated:
Working Code
Model
import Foundation
struct Parent {
private(set) var children: Array<Child>
private(set) var numberTimes: Int
init(numberOfChildren: Int) {
numberTimes = 0
children = []
for index in 0..<numberOfChildren {
children.append(Child(value: index))
}
toggleRandom()
}
mutating func choose(child: Child) {
if let chosenIndex = children.firstIndex(where: { $0.value == child.value }) {
children[chosenIndex].toggleIsSelected()
}
}
mutating func pressMe() {
numberTimes += 1
}
mutating func toggleRandom() {
children[Int.random(in: 0..<children.count)].toggleIsSelected()
}
}
struct Child: Identifiable {
let id: Int
private(set) var value: Int
private(set) var isSelected = false
init(value: Int) {
self.id = value
self.value = value
}
mutating func toggleIsSelected() {
isSelected.toggle()
}
}
ViewModel with raw children
import SwiftUI
class ParentViewModel: ObservableObject {
@Published var parent: Parent
init(numberOfChildren: Int) {
parent = Parent(numberOfChildren: numberOfChildren)
}
var children: Array<Child> {
parent.children
}
var parentText: String {
"'Press Me' pressed \(parent.numberTimes)"
}
func pressMe() {
parent.pressMe()
}
func toggleRandom() {
parent.toggleRandom()
}
}
View with raw children
import SwiftUI
struct ContentView: View {
@ObservedObject var parentViewModel: ParentViewModel
var body: some View {
VStack {
Text(parentViewModel.parentText).padding()
Button { parentViewModel.pressMe() }
label: { Text("Press Me") }
ForEach(parentViewModel.children, id: \.id) { child in
Text(String(child.value))
.foregroundColor(child.isSelected ? .white : .black)
.background(child.isSelected ? .black : .white)
}
Button { parentViewModel.toggleRandom() }
label: { Text("Toggle Random") }
}
}
}
Here's the code modified to use a view model for the child data elements. The model is the same as above, so only the view model and view code is different. This code continues to update the parent text correctly, but does not update the UI when child values change.
Not Working Code
ViewModel with child view model
import SwiftUI
class ParentViewModel: ObservableObject {
@Published var parent: Parent
private(set) var children: Array<ChildViewModel>
init(numberOfChildren: Int) {
parent = Parent(numberOfChildren: numberOfChildren)
children = []
for child in parent.children {
children.append(ChildViewModel(child: child))
}
}
var parentText: String {
"'Press Me' pressed \(parent.numberTimes)"
}
func pressMe() {
parent.pressMe()
}
func toggleRandom() {
parent.toggleRandom()
}
}
class ChildViewModel: ObservableObject, Identifiable {
@Published var child: Child
init(child: Child) {
self.child = child
}
var value: Int {
child.value
}
var isSelected: Bool {
child.isSelected
}
}
View with child view model
import SwiftUI
struct ContentView: View {
@ObservedObject var parentViewModel: ParentViewModel
var body: some View {
VStack {
Text(parentViewModel.parentText).padding()
Button { parentViewModel.pressMe() }
label: { Text("Press Me") }
ForEach(parentViewModel.children, id: \.id) { child in
ChildView(childViewModel: child)
}
Button { parentViewModel.toggleRandom() }
label: { Text("Toggle Random") }
}
}
}
struct ChildView: View {
@ObservedObject var childViewModel: ChildViewModel
var body: some View {
Text(String(childViewModel.value))
.foregroundColor(childViewModel.isSelected ? .white : .black)
.background(childViewModel.isSelected ? .black : .white)
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论