我已经组装了一个非常基本的应用程序。下面我给出了我的所有代码。
首先,这是我的主要应用程序结构,我使用
MyViewModel
作为唯一事实来源:
@main
struct TestApp: App {
@StateObject var myViewModel = MyViewModel()
var body: some Scene {
WindowGroup {
HomeView()
.environmentObject(myViewModel)
}
}
}
MyViewModel
非常基本:
class MyViewModel: ObservableObject {
@Published var users: [User] = []
func getUsers() {
self.users = [
User(id: 1, username: "Username", messages: [
Message(id: 1, userId: 1, date: "2024-04-16", message: "First comment"),
Message(id: 2, userId: 1, date: "2024-04-17", message: "Second comment"),
Message(id: 3, userId: 1, date: "2024-04-18", message: "Third comment"),
])
]
}
func changeDate(userId: Int, messageId: Int, date: String) {
if let userIndex = users.firstIndex(where: { $0.messages.contains(where: { $0.id == messageId }) }) {
if let messageIndex = users[userIndex].messages.firstIndex(where: { $0.id == messageId }) {
users[userIndex].messages[messageIndex][keyPath: \.date] = date
}
}
}
}
如您所见,我保留了
User
的列表。我还有一个函数changeDate()
,我稍后会讨论。
然后我有
HomeView
。这里我展示了所有User
的列表:
struct HomeView: View {
@EnvironmentObject var myViewModel: MyViewModel
var body: some View {
NavigationStack {
VStack {
ForEach(myViewModel.users, id: \.id) { user in
UserItem(user: user)
}
}
}
.task {
myViewModel.getUsers()
}
}
}
这是
UserItem
的样子:
struct UserItem: View {
var user: User
var body: some View {
NavigationLink(destination: UserPageView(user: user)) {
Text(user.username)
}
}
}
点击后,用户会转到
UserPageView
,我在其中显示用户消息列表,按天分组:
struct UserPageView: View {
var user: User
var body: some View {
ForEach(user.groupedMessagesByDay) { day in
VStack {
Text(day.date)
ForEach(day.messages, id: \.id) { message in
MessageView(message: message)
}
}
}
}
}
这是
User
模型,您可以在其中看到我如何按天对他们的消息进行分组:
struct User: Identifiable {
var id: Int
var username: String
var messages: [Message] {
didSet {
groupedMessagesByDay = getGroupedMessages()
}
}
var groupedMessagesByDay: [Day] = []
init(id: Int, username: String, messages: [Message]) {
self.id = id
self.username = username
self.messages = messages
self.groupedMessagesByDay = getGroupedMessages()
}
func getGroupedMessages() -> [Day] {
var messagesByDay: [String: [Message]] = [:]
for message in messages {
let date = message.date
if var messagesOnDate = messagesByDay[date] {
messagesOnDate.append(message)
messagesByDay[date] = messagesOnDate
} else {
messagesByDay[date] = [message]
}
}
var days: [Day] = []
for (date, messages) in messagesByDay {
let day = Day(date: date, messages: messages)
days.append(day)
}
days.sort { $0.date < $1.date }
return days
}
}
这是
Message
的样子:
struct Message: Identifiable {
let id: Int
let userId: Int
var date: String
var message: String
init(id: Int, userId: Int, date: String, message: String) {
self.id = id
self.userId = userId
self.date = date
self.message = message
}
}
这是
Day
的样子:
struct Day: Identifiable {
var id = UUID()
var date: String
var messages: [Message]
}
显示的每条消息都显示:
struct MessageView: View {
var message: Message
var body: some View {
NavigationLink(destination: MessagePageView(message: message)) {
Text("Show message page for id: \(message.id)")
}
}
}
当点击一条消息时,我会将它们带到
MessagePageView
:
struct MessagePageView: View {
@EnvironmentObject var myViewModel: MyViewModel
var message: Message
var body: some View {
// Show the current message date
Text(message.date)
// Tap button to change date to "2024-05-01"
Button(action: {
myViewModel.changeDate(userId: message.userId, messageId: message.id, date: "2024-05-01")
}, label: {
Text("Change message")
})
}
}
这就是我被困住的地方。
我预计,点击
Button
后,消息日期将更新为“2024-05-01”,并且页面上显示的日期 (Text(message.date)
) 也会更新,但事实并非如此。
我可以确认数据正在根据我的断点进行更新,但 UI 却没有。
我的
EnvironmentObject
实施做错了什么?我不明白什么?
如果您仍想使用当前的设置,那么您可以尝试 此方法使用额外的
@State var
,如示例代码所示。
这将更改 myViewModel
和本地 theMessage
显示。
简单地更新 var message: Message
不会更新视图。
struct MessagePageView: View {
@EnvironmentObject var myViewModel: MyViewModel
var message: Message
@State private var theMessage: Message? // <-- here
var body: some View {
// Show the current message date
Text(theMessage?.date ?? "") // <-- here
// Tap button to change date to "2024-05-01"
Button(action: {
// for testing
let newDate = "2024-05-01 " + String(UUID().uuidString.prefix(7))
theMessage?.date = newDate // <-- here
myViewModel.changeDate(userId: message.userId, messageId: message.id, date: newDate)
}, label: {
Text("Change message")
})
.onAppear {
theMessage = message // <-- here
}
}
}