mvvm - 为什么将 ViewModel 添加到我的 SwiftUI 应用程序后我的 UI 没有更新?
问题描述
我一直在关注SwiftUI 和 Firebase 上的 Peter Friese YT 系列,从第 1 部分和第 2 部分开始。现在我正在尝试将我在那里学到的知识转移到我自己的小测试应用程序中,特别是ViewModel
在组合中添加一个。
彼得的应用程序有一个单屏界面,而我想使用第二个屏幕为新元素输入几条数据,当用户单击“保存新元素”时,它被添加到数组中,然后返回到第一个屏幕.
在添加ViewModel
代码之前,它完全按照预期工作:创建新元素、添加到数组、更新 UI。它也适用于更新现有元素。
添加ViewModel
代码后,它几乎可以工作。创建新元素,添加到数组中。但是,用户界面没有更新,我不明白为什么不更新。
这是我的代码:
import Foundation
import SwiftUI
import Combine
struct Tournament: Identifiable {
var id = UUID().uuidString
var name: String
var location: String = "Franchises"
var date: Date = Date()
}
#if DEBUG
var blankTournament = Tournament(name: "")
var testTournamentData = [
Tournament(name: "Tournament 1"),
Tournament(name: "Tournament 2"),
Tournament(name: "Tournament 3"),
]
#endif
struct ContentView: View {
@ObservedObject var tournamentListVM = TournamentListVM()
var body: some View {
NavigationView {
VStack {
List(tournamentListVM.tournamentCellVMs) { tournamentCellVM in
NavigationLink(
destination: TournamentDetails(tournament: tournamentCellVM.tournament),
label: { TournamentCell(tournamentCellVM: tournamentCellVM) }
)}}
.navigationTitle("All in Two (\(tournamentListVM.tournamentCellVMs.count))")
.navigationBarItems(
trailing: NavigationLink(
destination: TournamentDetails(addingNewTournament: true))
{ Image(systemName: "plus") }
)}}}
struct TournamentCell: View {
@ObservedObject var tournamentCellVM: TournamentCellVM
var body: some View {
VStack(alignment: .leading) {
Text(tournamentCellVM.tournament.name)
}
}
}
struct TournamentDetails: View {
@Environment(\.presentationMode) var presentationMode // used to dismiss the view
@ObservedObject var tournamentListVM = TournamentListVM()
@State var tournament = blankTournament
var addingNewTournament: Bool = false
var index: Int? // needed when updating an existing record
var body: some View {
Form {
Section(header: Text("Tournament Info")) {
TextField("Name", text: $tournament.name)
TextField("Location", text: $tournament.location)
DatePicker(selection: $tournament.date, displayedComponents: .date) { Text("Date") }
}
Section { Button(action: {
if addingNewTournament { tournamentListVM.createTournament(tournament) }
else { tournamentListVM.updateTournament(tournament) }
presentationMode.wrappedValue.dismiss()
}) { Text(addingNewTournament ? "Save Tournament" : "Update Tournament") }
}}}}
class TournamentListVM: ObservableObject {
@Published var tournamentCellVMs = [TournamentCellVM]()
private var cancellables = Set<AnyCancellable>()
init() {
self.tournamentCellVMs = testTournamentData.map { tournament in
TournamentCellVM(tournament: tournament)
}
}
func createTournament(_ tournament: Tournament) {
print (tournamentCellVMs.count)
tournamentCellVMs.append(TournamentCellVM(tournament: tournament))
print (tournamentCellVMs.count)
}
func updateTournament(_ tournament: Tournament) {
}
}
class TournamentCellVM: ObservableObject, Identifiable {
@Published var tournament: Tournament
var id = ""
private var cancellables = Set<AnyCancellable>()
init(tournament: Tournament) {
self.tournament = tournament
$tournament
.map { tournament in tournament.id }
.assign(to: \.id, on: self)
.store(in: &cancellables)
}}
解决方案
当我准备发布我的问题时,SO 向我指出了其他可能类似的问题。其中一位帮助我找到了问题的答案。我在这里发布我的解决方案作为我自己和他人的资源。
在那篇文章中,我了解到如果列表动态更改,那么您必须使用.indices
.
这是我所做的更改,现在它可以工作了:
- 在
struct ContentView
:
List(tournamentListVM.tournamentCellVMs) { tournamentCellVM in
NavigationLink(
destination: TournamentDetails(tournament: tournamentCellVM.tournament),
label: { TournamentCell(tournamentCellVM: tournamentCellVM) }
)}}
.navigationTitle("All in Two (\(tournamentListVM.tournamentCellVMs.count))")
.navigationBarItems(
trailing: NavigationLink(
destination: TournamentDetails(addingNewTournament: true))
{ Image(systemName: "plus") }
)}}}
变成了
List {
ForEach (tournamentListVM.tournamentCellVMs.indices, id: \.self) { index in
NavigationLink(
destination: TournamentDetails(tournamentListVM: tournamentListVM, tournament: tournamentListVM.tournamentCellVMs[index].tournament),
label: { TournamentCell(tournamentCellVM: tournamentListVM.tournamentCellVMs[index]) }
)}}}
.navigationTitle("All in Two (\(tournamentListVM.tournamentCellVMs.count))")
.navigationBarItems(
trailing: NavigationLink(
destination: TournamentDetails(tournamentListVM: tournamentListVM, addingNewTournament: true))
{ Image(systemName: "plus") }
)}}}
- 在
struct TournamentDetails
:
@ObservedObject var tournamentListVM = TournamentListVM()
变成了
@ObservedObject var tournamentListVM: TournamentListVM
- 在
class TournamentListVM
@Published var tournamentCellVMs = [TournamentCellVM]()
变成了
@Published var tournamentCellVMs: [TournamentCellVM]
而已。而且效果很好。
推荐阅读
- java - Receiving Null while Parsing JSON
- c# - 从字节数组读取/写入 32 位 32 位 DateTime
- python - How to set values for particular cells in pandas.Dataframe correctly?
- javafx - javafx:在列中显示超链接并打开一个新窗口
- oauth - Aouth 2.0 谁来管理 2 认证类型?
- angular - 角度获取子属性不仅是id
- python - 如何使用 Python 沿 x 轴分箱 2D 数据
- selenium - Selenium:捕获 NoSuchElementException 出现在可点击元素上是否有用?
- java - Jackson: understand if source JSON is an array or an object
- c - C language: memory allocation of a structure of two char* pointers