在watchOS上使用SwiftUI进行意外(自动)导航
我有一个简单的watchOS 6.2.8 SwiftUI应用程序,其中我向用户提供了一个消息列表 这些消息被建模为类,并具有标题、正文和类别名称。我还有一个Category对象,它是这些消息的视图,只显示特定的类别名称 我特别提到watchOS 6.2.8,因为它似乎与其他平台上的SwiftUI行为有所不同 类别本身是一个在watchOS上使用SwiftUI进行意外(自动)导航,swiftui,watchkit,swiftui-navigationlink,Swiftui,Watchkit,Swiftui Navigationlink,我有一个简单的watchOS 6.2.8 SwiftUI应用程序,其中我向用户提供了一个消息列表 这些消息被建模为类,并具有标题、正文和类别名称。我还有一个Category对象,它是这些消息的视图,只显示特定的类别名称 我特别提到watchOS 6.2.8,因为它似乎与其他平台上的SwiftUI行为有所不同 类别本身是一个@observeObject并发布消息,因此当类别更新时(如在后台),显示类别消息列表的视图也将更新。(这很有效) 为了存储这些消息,我有一个简单的MessageStore,它
@observeObject
并发布消息
,因此当类别更新时(如在后台),显示类别消息列表的视图也将更新。(这很有效)
为了存储这些消息,我有一个简单的MessageStore
,它是一个@observeobject
,如下所示:
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上没有正确传递环境对象会出现问题)
这个故事使消息
和类别
保持同步。添加设置了类别名称的新邮件时,它还将在类别
列表中创建或更新类别
对象
在我的主要观点中,我提出了两件事:
- 所有消息
,转到视图以显示所有消息导航链接
- 对于每个类别,我都会显示一个
,该链接会转到一个视图,以仅显示该特定类别中的消息NavigationLink
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))
}
}
抱歉,我知道这是很多代码,但我觉得我需要提供足够的上下文和可见性来显示这里发生了什么
完整项目在-我不认为更多的代码是相关的,但如果是,请让我知道,我将把它移到这里的问题。问题在于更新的
ForEach
,这导致重新创建列表,从而断开链接。这看起来像是SwiftUI缺陷,所以值得向苹果提交反馈
经过测试的解决方法是将所有消息导航链接移出列表(看起来有点不同,但可能是合适的)。使用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
是否有理由消息是一个类?根据我的经验,SwiftUI数据对象作为结构更好地工作,除非您需要引用语义,而您似乎不需要引用语义。我自己也有类似的结果,我没有使用列表
,而是使用滚动视图
——那时UI有点不同,但奇怪的行为消失了。
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