导航回溯 (Navigation Backport)

此软件包使用旧版 SwiftUI 中可用的导航 API(例如 NavigationViewNavigationLink)来重新创建 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)

可以通过环境访问 Navigator 对象,从而可以访问当前的导航路径。 可以通过环境访问导航器,例如,对于由 NBNavigationPath 支持的堆栈

@EnvironmentObject var navigator: PathNavigator

或者对于由 Array 支持的堆栈,例如 [ScreenType]

@EnvironmentObject var navigator: Navigator<ScreenType>

除了允许您检查路径元素之外,导航器还可以用于推送新屏幕、弹出、弹出到特定屏幕或弹出到根屏幕。

导航功能

无论是与 ArrayNBNavigationPath 还是 Navigator 交互,都有许多实用功能可用于更轻松的导航,例如

path.push(Profile(name: "John"))

path.pop()

path.popToRoot()

path.popTo(Profile.self)

请注意,如果您想在 Array 上使用这些方法,请确保 ArrayElement 符合 NBScreen,这是一个继承自 Hashable 而没有添加任何附加要求的协议。 这避免了使用特定于导航的 API 污染所有数组。

深度链接 (Deep-linking)

NavigationStack 之前,SwiftUI 不支持在单个状态更新中推送多个屏幕,例如,当深度链接到导航层次结构中多个深度的屏幕时。 NavigationBackport 解决了这个限制:您可以进行任何此类路径更改,并且如果需要,该库将在后台将更大的更新分解为 SwiftUI 支持的一系列较小的更新,并在其间添加延迟。 例如,以下在一个状态更新中推送三个屏幕的代码将在需要时逐个推送屏幕

  path.append(Screen.orders)
  path.append(Screen.editOrder(id: id))
  path.append(Screen.confirmChanges(orderId: id))

这种情况仅在必要时发生:在支持 NavigationStack 的 SwiftUI 版本上,所有三个屏幕都将在一个更新中成功推送。

支持 iOS/tvOS 13

此库的目标是 iOS/tvOS 14 及更高版本,因为它使用 StateObject,该对象在 iOS/tvOS 13 上不可用。 但是,有一个 ios13 分支,它使用 SwiftUIBackports 的回溯 StateObject,以便它也可以在 iOS/tvOS 13 上运行。

FlowStacks

想要进一步升级您的导航 API 吗? FlowStacks 增强了这些熟悉的 API,使您可以额外地从一个统一的界面驱动 sheet 和全屏覆盖导航。


ko-fi