NavigationSplitView – SwiftUI.AnyNavigationPath.Error.comparisonTypeMismatch

huangapple go评论227阅读模式
英文:

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 NavigationStacks, 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()
}

huangapple
  • 本文由 发表于 2023年5月7日 14:19:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/76192465.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定