此软件包是 SNAP 套件的一部分。
定义 SwiftUI 应用的导航结构,并使其与其呈现方式解耦。
SnapNavigation 允许您以通用的方式定义应用程序的导航层次结构。可以通过选择、推送(在选定的或当前模态堆栈上)或模态呈现来显示屏幕。它还允许您深度链接到特定的屏幕,同时设置整个层次结构。
该软件包提供 SnapNavigationDestination
来定义屏幕,并提供 SnapNavigationProvider
来定义如何在它们之间导航。
在您的 App 定义中使用 SnapNavigationApp
,以便 SnapNavigation 处理呈现和窗口管理。它通过 Environment 提供一个处理程序 \.navigator
,以触发 NavigatorAction
。它支持不同的呈现样式,如选项卡或单页,可以动态更改,而不会丢失导航状态。
Window = 应用窗口 Scene = NavigationStack Destination = 屏幕
支持
// TODO: 键盘导航,更好的辅助功能支持,侧边栏重新排序
演示项目 显示了一个具有 3 个顶级项目的导航层次结构,可供选择。它允许无限的项目被推送或呈现为模态,并且有一些深度链接可以导航到更复杂的状态。
定义 App 可以导航到的目标
enum Destination: SnapNavigationDestination {
case triangle, rectangle, circle
var definition: SnapNavigation.ScreenDefinition<Self> {
switch self {
case .triangle: .init(title: "Triangle", systemIcon: "triangle")
...
}
}
}
实现一个 SnapNavigationProvider
来定义可访问目标的结构
struct NavigationProvider: SnapNavigationProvider {
var initialSelection: Destination { .triangle }
var selectableDestinations: [Destination] { [.triangle, .rectangle, .circle] }
func parent(of destination: Destination) -> Destination? {
switch destination {
case .triangle, .rectangle, .circle: nil
}
}
}
在您的 @main App 定义中使用 SnapNavigationApp
@main
struct SnapNavigationDemoApp: App {
var body: some Scene {
SnapNavigationApp(provider: NavigationProvider()) { window, content in
content
.navigationStyle(.single)
// ... setup more global stuff ...
}
}
}```
Use `\.navigator` to navigate in your App:
@Environment(\.navigator) private var navigator
navigator(.present(Destination.infinity))
## Considerations
### TabSection
iOS 18 supports to group multiple Tabs into a TabSection: While the sidebar is visible, the Tabs are visible below the section header. While the TabBar is visible, only the section header is visible as a tab.
This causes ambiguous state when switching size classes or hiding the sidebar. I tried a few things, like manually adding the Section on the NavigationStack. But was not really happy with any of them.
Decision: Not supporting TabSection for now.
### .fullScreenCover()
Supporting a mix of .sheet() and .fullScreenCover() causes some animation issues in deeplink handling.
Decision: Not supporting .fullScreenCover() for now. Modal presentation uses .sheet().
### macOS: TabView sidebarAdaptable clicking label does not select
Happening since macOS 15.1 [FB15680632](https://github.com/simonnickel/FB15680632-SwiftUImacOS-TabView-sidebarAdaptable-labelNotSelectable)
// TODO FB15680632: Check if issue is solved
### macOS: TabView with .sidebarAdaptable does not maintain state of Tab / Sidebar Item.
Decision: Did not find a way to maintain the navigation state, not worth it at the moment. Reconsider in the future.
### AppDestinations + Navigator: Navigator Actions via Environment do not support a generic constraint to a Destination Type.
Decision: Implementation is possible, but causes a lot of boilerplate and redundancy. Left it out for now to encourage using different Destination enums for Features.
// TODO: Define FullScreenCover as additional PresentationStyle, which can only be present once as last item with a path to show, no modals. (Or even with its own complete SnapNavigationView and State).
// TODO: Fix tapping in background when 2 modals are open closes all modals. (on iPad)