【发布时间】:2020-11-08 07:09:39
【问题描述】:
我有一个简单的 watchOS 6.2.8 SwiftUI 应用程序,我在其中向用户显示了一个消息列表。
这些消息被建模为类,并具有标题、正文和类别名称。我还有一个 Category 对象,它是这些消息的视图,只显示特定的类别名称。
我特别提到 watchOS 6.2.8,因为 SwiftUI 的行为似乎与其他平台上的有所不同。
class Message: Identifiable {
let identifier: String
let date: Date
let title: String
let body: String
let category: String
var id: String {
identifier
}
init(identifier: String, date: Date, title: String, body: String, category: String) {
self.identifier = identifier
self.date = date
self.title = title
self.body = body
self.category = category
}
}
class Category: ObservableObject, Identifiable {
let name: String
@Published var messages: [Message] = []
var id: String {
name
}
init(name: String, messages: [Message] = []) {
self.name = name
self.messages = messages
}
}
Category 本身是一个@ObservableObject 并发布messages,因此当一个类别被更新时(比如在后台),显示类别消息列表的视图也会更新。 (这很好用)
为了存储这些消息,我有一个简单的MessageStore,它是一个看起来像这样的@ObservableObject:
class MessageStore: ObservableObject {
@Published var messages: [Message] = []
@Published var categories: [Category] = []
static let sharedInstance = MessageStore()
func insert(message: Message) throws { ... mutage messages and categories ... }
func delete(message: Message) throws { ... mutage messages and categories ... }
}
(为简单起见,我使用单例,因为在 watchOS 上无法正确传递环境对象存在问题)
故事使messages 和categories 保持同步。当添加设置了类别名称的新消息时,它还将在categories 列表中创建或更新Category 对象。
在我的主要观点中,我提出了两件事:
-
所有消息
NavigationLink转到视图以显示所有消息 - 对于每个类别,我会显示一个
NavigationLink,它会转到一个视图以显示该特定类别中的消息。
这一切都有效,令人惊讶。但是发生了一件我不明白的非常奇怪的事情。 (第一个 SwiftUI 项目)
当我进入所有消息列表并删除包含特定类别的所有消息时,当我导航回主视图时会发生意外情况。
首先我观察到该类别已从列表中正确删除。
然后,主视图自动快速导航到所有消息列表,然后返回。
最后一部分让我..疯了..我不明白为什么会这样。从数据的角度来看,一切看起来都不错 - 消息已被删除,类别也已删除。自动导航后的最终 UI 状态看起来也不错 - 所有消息的消息计数是正确的,现在消息为零的类别不再显示在列表中。
这是主要ContentView 和AllMessagesView 的代码。如果有帮助,我当然可以在这里发布完整的代码。
struct AllMessagesView: View {
@ObservedObject var messageStore = MessageStore.sharedInstance
@ViewBuilder
var body: some View {
if messageStore.messages.count == 0 {
Text("No messages").multilineTextAlignment(.center)
.navigationBarTitle("All Messages")
} else {
List {
ForEach(messageStore.messages) { message in
MessageCellView(message: message)
}.onDelete(perform: deleteMessages)
}
.navigationBarTitle("All Messages")
}
}
func deleteMessages(at offsets: IndexSet) {
for index in offsets {
do {
try messageStore.delete(message: messageStore.messages[index])
} catch {
NSLog("Failed to delete message: \(error.localizedDescription)")
}
}
}
}
//
struct CategoryMessagesView: View {
@ObservedObject var messageStore = MessageStore.sharedInstance
@ObservedObject var category: Category
var body: some View {
Group {
if category.messages.count == 0 {
Text("No messages in category “\(category.name)”").multilineTextAlignment(.center)
} else {
List {
ForEach(category.messages) { message in
MessageCellView(message: message)
}.onDelete(perform: deleteMessages)
}
}
}.navigationBarTitle(category.name)
}
func deleteMessages(at offsets: IndexSet) {
for index in offsets {
do {
try messageStore.delete(message: category.messages[index])
} catch {
NSLog("Cannot delete message: \(error.localizedDescription)")
}
}
}
}
struct ContentView: View {
@ObservedObject var messageStore = MessageStore.sharedInstance
var body: some View {
List {
Section {
NavigationLink(destination: AllMessagesView()) {
HStack {
Image(systemName: "tray.2")
Text("All Messages")
Spacer()
Text("\(messageStore.messages.count)")
.font(messageCountFont())
.bold()
.layoutPriority(1)
.foregroundColor(.green)
}
}
}
Section {
Group {
if messageStore.categories.count > 0 {
Section {
ForEach(messageStore.categories) { category in
NavigationLink(destination: CategoryMessagesView(category: category)) {
HStack {
Image(systemName: "tray") // .foregroundColor(.green)
Text("\(category.name)").lineLimit(1).truncationMode(.tail)
Spacer()
Text("\(category.messages.count)")
.font(self.messageCountFont())
.bold()
.layoutPriority(1)
.foregroundColor(.green)
}
}
}
}
} else {
EmptyView()
}
}
}
}
}
// TODO This is pretty inefficient
func messageCountFont() -> Font {
let font = UIFont.preferredFont(forTextStyle: .caption1)
return Font(font.withSize(font.pointSize * 0.75))
}
}
抱歉,我知道这是很多代码,但我觉得我需要提供足够的上下文和可见性来展示这里发生的事情。
https://github.com/st3fan/LearningSwiftUI/tree/master/MasterDetail 的完整项目 - 我不认为更多代码是相关的,但如果是,请告诉我,我会在此处将其移至问题中。
【问题讨论】:
-
Message是一个类有什么原因吗?根据我的经验,SwiftUI 数据对象作为结构更好地工作,除非您需要引用语义,而您似乎不需要。
标签: swiftui watchkit swiftui-navigationlink