为啥将 ViewModel 添加到我的 SwiftUI 应用程序后我的 UI 没有更新?
Posted
技术标签:
【中文标题】为啥将 ViewModel 添加到我的 SwiftUI 应用程序后我的 UI 没有更新?【英文标题】:Why doesn't my UI update after adding a ViewModel to my SwiftUI app?为什么将 ViewModel 添加到我的 SwiftUI 应用程序后我的 UI 没有更新? 【发布时间】:2021-04-30 13:35:16 【问题描述】:我一直在关注Peter Friese YT series on SwiftUI & Firebase,从第 1 部分和第 2 部分开始。现在我正在尝试将我在那里学到的知识转移到我自己的小测试应用程序中,特别是在组合中添加 ViewModel
。
彼得的应用程序有一个单屏界面,而我想使用第二个屏幕为新元素输入几条数据,当用户单击“保存新元素”时,它被添加到数组中,然后返回到第一个屏幕。
在添加 ViewModel
代码之前,它完全按照预期工作:创建新元素、添加到数组、更新 UI。它也适用于更新现有元素。
添加ViewModel
代码后,它几乎可以工作了。创建新元素,添加到数组中。 但是,UI 没有更新,我不明白为什么不更新。
这是我的代码:
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)
【问题讨论】:
【参考方案1】:当我准备发布我的问题时,SO 向我指出了其他可能类似的问题。 One of them helped me find the answer to my question.我在这里发布我的解决方案,作为自己和他人的资源。
在那篇文章中,我了解到如果列表动态变化,那么您必须使用.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]
就是这样。而且效果很好。
【讨论】:
以上是关于为啥将 ViewModel 添加到我的 SwiftUI 应用程序后我的 UI 没有更新?的主要内容,如果未能解决你的问题,请参考以下文章
为啥我需要调用 removeView() 才能将视图添加到我的 LinearLayout
SwiftUI MVVM 将变量从 ViewModel 传递到 API 服务
如何使用 swift 将相同的背景图像添加到我的 ios 应用程序中的所有视图?
为啥每当我尝试将图像添加到我的 django 项目时都会出错?