尽管使用 ObservableObject 和 EnvironmentObject 更新数据,但视图未更新

问题描述 投票:0回答:1

我已经组装了一个非常基本的应用程序。下面我给出了我的所有代码。

首先,这是我的主要应用程序结构,我使用

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
实施做错了什么?我不明白什么?

swift swiftui
1个回答
0
投票

如果您仍想使用当前的设置,那么您可以尝试 此方法使用额外的

@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
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.