NavigationBackport 0.9.3

NavigationBackport 0.9.3

John Morgan 维护。



  • 作者:
  • John Patrick Morgan

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 的绑定来初始化一个 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>

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

导航功能

无论您是与之交互 ArrayNBNavigationPath 还是 Navigator,都有一系列实用功能可用于简化导航,例如

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

path.pop()

path.popToRoot()

path.popTo(Profile.self)

请注意,如果您想在 Array 上使用这些方法,请确保该 ArrayElement 遵循从 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 13

本库针对iOS/tvOS 14及以上版本,因为它使用了StateObject,而这些在iOS/tvOS 13中不可用。然而,存在一个ios13分支,它使用SwiftUIBackports回退的StateObject,从而使其在iOS/tvOS 13上也能运行。