此包使用旧版 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
的绑定来初始化一个 NBNavigationStack
,用绑定为绑定到 NBNavigationPath
的绑定来初始化,或者根本不使用绑定。
点击展开示例
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
遵循从 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上也能运行。