我有一个简单的 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
}
}
类别本身是@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
保持同步。添加了设置了类别名称的新消息时,它还将在Category
列表中创建或更新categories
对象。
在我的主要观点中,我提出两件事:
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的完整项目-我认为没有更多相关的代码,但是如果有,请告诉我,我将其移至问题中。
答案 0 :(得分:0)
问题出在更新的ForEach
中,这导致重新创建List
并因此中断了链接。这看起来像SwiftUI的缺陷,因此值得向Apple提交反馈。
经过测试的解决方法是将“所有消息”导航链接移出列表(看起来有些不同,但可能合适)。经过Xcode 12 / watchOS 7.0的测试
struct ContentView: View {
@ObservedObject var messageStore = MessageStore.sharedInstance
var body: some View {
VStack {
NavigationLink(destination: AllMessagesView()) {
HStack {
Image(systemName: "tray.2")
Text("All Messages")
Spacer()
Text("\(messageStore.messages.count)")
.font(messageCountFont())
.bold()
.layoutPriority(1)
.foregroundColor(.green)
}
}
List {
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)
}
}
}
}
}
}
// ... other code