英文:
NavigationSplitView - SwiftUI.AnyNavigationPath.Error.comparisonTypeMismatch
问题
Receiving error:
```SwiftUI/NavigationColumnState.swift:520: Fatal error: 'try!' expression unexpectedly raised an error: SwiftUI.AnyNavigationPath.Error.comparisonTypeMismatch`
当NavigationSplitView的详细视图发生更改,并且从中移动的详细视图具有NavigationStack(path: ...)
时出现此错误。
这似乎是一个错误,尽管通常当我认为出现错误时,我可能做了一些愚蠢的事情。如果有人能找到解决方法或修复方法,我会很高兴知道。
Xcode: 版本 14.3 (14E222b)
模拟器: iPad Air (第五代) / My Mac (设计用于 iPad)
模型
import SwiftUI
final class AppModel: ObservableObject {
@Published var appNavigation: AppNavigation? = .lists
@Published var listsNavigation: [ListsNavigation] = []
@Published var settingsNavigation: [SettingsNavigation] = []
}
enum AppNavigation {
case lists, settings
}
enum SettingsNavigation {
case settings1, settings2
}
enum ListsNavigation {
case favourites, notes
}
App.swift
import SwiftUI
@main
struct splittyApp: App {
@StateObject private var model = AppModel()
var body: some Scene {
WindowGroup {
NavigationSplitView {
List(selection: $model.appNavigation) {
NavigationLink(value: AppNavigation.lists) {
Label("Lists", systemImage: "list.clipboard")
}
NavigationLink(value: AppNavigation.settings) {
Label("Settings", systemImage: "gearshape")
}
}
} detail: {
switch model.appNavigation {
case .lists: ListsView()
case .settings: SettingsView()
case .none: Text("Error")
}
}
.environmentObject(model)
}
}
}
ListsView
import SwiftUI
struct ListsView: View {
@EnvironmentObject private var model: AppModel
var body: some View {
NavigationStack(path: $model.listsNavigation) {
List {
NavigationLink(value: ListsNavigation.favourites) {
Text("Favourites")
}
NavigationLink(value: ListsNavigation.notes) {
Text("Notes")
}
}
.navigationDestination(for: ListsNavigation.self) { value in
switch value {
case .favourites: Text("Favourites View")
case .notes: Text("Notes View")
}
}
}
}
}
SettingsView
import SwiftUI
struct SettingsView: View {
@EnvironmentObject private var model: AppModel
var body: some View {
NavigationStack(path: $model.settingsNavigation) {
List {
NavigationLink(value: SettingsNavigation.settings1) {
Text("Settings 1")
}
NavigationLink(value: SettingsNavigation.settings2) {
Text("Settings 2")
}
}
.navigationDestination(for: SettingsNavigation.self) { value in
switch value {
case .settings1: Text("Settings 1")
case .settings2: Text("Settings 2")
}
}
}
}
}
示例项目: https://www.dropbox.com/s/jlxct4qeac8kt75/splitty.zip?dl=0
复现步骤:
- 在 iPad 上启动应用程序
- 在主应用程序导航 (lists/settings) 之间点击
英文:
Receiving error:
SwiftUI/NavigationColumnState.swift:520: Fatal error: 'try!' expression unexpectedly raised an error: SwiftUI.AnyNavigationPath.Error.comparisonTypeMismatch`
When a NavigationSplitView's detail view changes, and the detail view moving from has a NavigationStack(path: ...)
.
This seems like a bug, though usually when I think that I've done something silly. Would love to know a workaround or fix if anyone can figure it out.
Xcode: Version 14.3 (14E222b)
Simulators: iPad Air (5th Generation) / My Mac (designed for iPad)
Models
import SwiftUI
final class AppModel: ObservableObject {
@Published var appNavigation: AppNavigation? = .lists
@Published var listsNavigation: [ListsNavigation] = []
@Published var settingsNavigation: [SettingsNavigation] = []
}
enum AppNavigation {
case lists, settings
}
enum SettingsNavigation {
case settings1, settings2
}
enum ListsNavigation {
case favourites, notes
}
App.swift
import SwiftUI
@main
struct splittyApp: App {
@StateObject private var model = AppModel()
var body: some Scene {
WindowGroup {
NavigationSplitView {
List(selection: $model.appNavigation) {
NavigationLink(value: AppNavigation.lists) {
Label("Lists", systemImage: "list.clipboard")
}
NavigationLink(value: AppNavigation.settings) {
Label("Settings", systemImage: "gearshape")
}
}
} detail: {
switch model.appNavigation {
case .lists: ListsView()
case .settings: SettingsView()
case .none: Text("Error")
}
}
.environmentObject(model)
}
}
}
*ListsView
import SwiftUI
struct ListsView: View {
@EnvironmentObject private var model: AppModel
var body: some View {
NavigationStack(path: $model.listsNavigation) {
List {
NavigationLink(value: ListsNavigation.favourites) {
Text("Favourites")
}
NavigationLink(value: ListsNavigation.notes) {
Text("Notes")
}
}
.navigationDestination(for: ListsNavigation.self) { value in
switch value {
case .favourites: Text("Favourites View")
case .notes: Text("Notes View")
}
}
}
}
}
SettingsView
import SwiftUI
struct SettingsView: View {
@EnvironmentObject private var model: AppModel
var body: some View {
NavigationStack(path: $model.settingsNavigation) {
List {
NavigationLink(value: SettingsNavigation.settings1) {
Text("Settings 1")
}
NavigationLink(value: SettingsNavigation.settings2) {
Text("Settings 2")
}
}
.navigationDestination(for: SettingsNavigation.self) { value in
switch value {
case .settings1: Text("Settings 1")
case .settings2: Text("Setings 2")
}
}
}
}
}
Example Project: https://www.dropbox.com/s/jlxct4qeac8kt75/splitty.zip?dl=0
Steps to reproduce:
- Launch app on iPad
- Tap between main app navigation (lists/settings)
答案1
得分: 3
错误发生是因为您在详细视图“NavigationStack”中使用了两种不同的导航枚举类型。
尽管您定义了两个“NavigationStack”,一个在每个详细子视图中,但SwiftUI仅创建一个。因此,这两个代码块的行为相同:
NavigationSplitView {
...
} detail: {
View1()
View2()
}
View1() ... {
NavigationStack(path: $pathA) { ... }
}
View2() ... {
NavigationStack(path: $pathA) { ... }
}
与以下代码块相同:
NavigationSplitView {
...
} detail: {
NavigationStack(path: $pathA ? $pathB ?) { // 哪种类型?
View1()
View2()
}
}
View1() ... {
}
View2() ... {
}
第二个版本使SwiftUI为什么感到困扰变得明显。它在路径中找到了两种不同类型的值。
文档中有提到,但表述较为晦涩:
您还可以将NavigationStack嵌入到列中。...在同一列中激活链接会将视图添加到堆栈中。无论哪种方式,链接必须呈现一个具有堆栈相应navigationDestination(for:destination:)修饰符的数据类型。
解决方案:
问题的解决方案是为两个路径变量使用特殊的类型擦除NavigationPath
。它正是为了解决这个问题而引入的:
final class AppModel: ObservableObject {
@Published var appNavigation: AppNavigation? = .lists
@Published var listsNavigation = NavigationPath()
@Published var settingsNavigation = NavigationPath()
}
英文:
The error occurs because you use two different navigation enum types in the detail view NavigationStack
.
Even though you define two NavigationStack
s, one in each detail subview, SwiftUI creates only one. So these two codes act identical:
NavigationSplitView {
...
} detail: {
View1()
View2()
}
View1() ... {
NavigationStack(path: $pathA) { ... }
}
View2() ... {
NavigationStack(path: $pathA) { ... }
}
is identical to:
NavigationSplitView {
...
} detail: {
NavigationStack(path: $pathA ? $pathB ?) { // which type?
View1()
View2()
}
}
View1() ... {
}
View2() ... {
}
The second version makes it apparent, why SwiftUI struggles. It finds values of two different types in the path.
The documentation says it, but in a quite cryptical way:
> You can also embed a NavigationStack in a column. ... Activating a
> link in the same column adds a view to the stack. Either way, the
> link must present a data type for which the stack has a corresponding
> navigationDestination(for:destination:) modifier.
Solution:
The solution for the problem is to use the special type erased NavigationPath
for both path vars. It has been introduced exactly for this purpose:
final class AppModel: ObservableObject {
@Published var appNavigation: AppNavigation? = .lists
@Published var listsNavigation = NavigationPath()
@Published var settingsNavigation = NavigationPath()
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论