此软件包使用旧版 SwiftUI 中可用的导航 API(例如 NavigationView
和 NavigationLink
)来重新创建 WWDC22 中引入的新的 NavigationStack
API,以便您可以开始在旧版本的 iOS、tvOS、macOS 和 watchOS 上使用这些 API。 在支持 NavigationStack
的操作系统版本上运行时,底层将使用 NavigationStack
。
✅ NavigationStack
-> NBNavigationStack
✅ NavigationLink
-> NBNavigationLink
✅ NavigationPath
-> NBNavigationPath
✅ navigationDestination
-> nbNavigationDestination
✅ NavigationPath.CodableRepresentation
-> NBNavigationPath.CodableRepresentation
您现在可以迁移到这些 API,并且当您最终提高部署目标时,您可以删除此库并轻松迁移到其 SwiftUI 等效项。NavigationStack
的完整 API 已被复制,因此您可以使用绑定到 Array
的绑定、绑定到 NBNavigationPath
的绑定,或根本没有绑定的方式来初始化 NBNavigationStack
。
import NavigationBackport
import SwiftUI
struct ContentView: View {
@State var path = NBNavigationPath()
var body: some View {
NBNavigationStack(path: $path) {
HomeView()
.nbNavigationDestination(for: NumberList.self, destination: { numberList in
NumberListView(numberList: numberList)
})
.nbNavigationDestination(for: Int.self, destination: { number in
NumberView(number: number)
})
.nbNavigationDestination(for: EmojiVisualisation.self, destination: { visualisation in
EmojiView(visualisation: visualisation)
})
}
}
}
struct HomeView: View {
var body: some View {
VStack(spacing: 8) {
NBNavigationLink(value: NumberList(range: 0 ..< 100), label: { Text("Pick a number") })
}.navigationTitle("Home")
}
}
struct NumberList: Hashable {
let range: Range<Int>
}
struct NumberListView: View {
let numberList: NumberList
var body: some View {
List {
ForEach(numberList.range, id: \.self) { number in
NBNavigationLink("\(number)", value: number)
}
}.navigationTitle("List")
}
}
struct NumberView: View {
@EnvironmentObject var navigator: PathNavigator
let number: Int
var body: some View {
VStack(spacing: 8) {
Text("\(number)")
NBNavigationLink(
value: number + 1,
label: { Text("Show next number") }
)
NBNavigationLink(
value: EmojiVisualisation(emoji: "🐑", count: number),
label: { Text("Visualise with sheep") }
)
Button("Go back to root", action: { navigator.popToRoot() })
}.navigationTitle("\(number)")
}
}
struct EmojiVisualisation: Hashable {
let emoji: String
let count: Int
var text: String {
Array(repeating: emoji, count: count).joined()
}
}
struct EmojiView: View {
let visualisation: EmojiVisualisation
var body: some View {
Text(visualisation.text)
.navigationTitle("Visualise \(visualisation.count)")
}
}
除了复制新的 NavigationStack
API 的标准功能外,还添加了一些有用的实用程序。
可以通过环境访问 Navigator
对象,从而可以访问当前的导航路径。 可以通过环境访问导航器,例如,对于由 NBNavigationPath 支持的堆栈
@EnvironmentObject var navigator: PathNavigator
或者对于由 Array 支持的堆栈,例如 [ScreenType]
@EnvironmentObject var navigator: Navigator<ScreenType>
除了允许您检查路径元素之外,导航器还可以用于推送新屏幕、弹出、弹出到特定屏幕或弹出到根屏幕。
无论是与 Array
、NBNavigationPath
还是 Navigator
交互,都有许多实用功能可用于更轻松的导航,例如
path.push(Profile(name: "John"))
path.pop()
path.popToRoot()
path.popTo(Profile.self)
请注意,如果您想在 Array
上使用这些方法,请确保 Array
的 Element
符合 NBScreen
,这是一个继承自 Hashable 而没有添加任何附加要求的协议。 这避免了使用特定于导航的 API 污染所有数组。
在 NavigationStack
之前,SwiftUI 不支持在单个状态更新中推送多个屏幕,例如,当深度链接到导航层次结构中多个深度的屏幕时。 NavigationBackport
解决了这个限制:您可以进行任何此类路径更改,并且如果需要,该库将在后台将更大的更新分解为 SwiftUI 支持的一系列较小的更新,并在其间添加延迟。 例如,以下在一个状态更新中推送三个屏幕的代码将在需要时逐个推送屏幕
path.append(Screen.orders)
path.append(Screen.editOrder(id: id))
path.append(Screen.confirmChanges(orderId: id))
这种情况仅在必要时发生:在支持 NavigationStack
的 SwiftUI 版本上,所有三个屏幕都将在一个更新中成功推送。
此库的目标是 iOS/tvOS 14 及更高版本,因为它使用 StateObject
,该对象在 iOS/tvOS 13 上不可用。 但是,有一个 ios13
分支,它使用 SwiftUIBackports 的回溯 StateObject,以便它也可以在 iOS/tvOS 13 上运行。
想要进一步升级您的导航 API 吗? FlowStacks 增强了这些熟悉的 API,使您可以额外地从一个统一的界面驱动 sheet 和全屏覆盖导航。