这个仓库提供了一种新的、简单的方法来描述路由器。
我将应用程序流程视为所有可能的屏幕状态的树。从这个角度来看,导航就是选择这棵树的一个节点。
例如,有一个具有如下屏幕层次结构的应用程序
TabView
┌────────────┼────────────┐
Tab1 Tab2 NavigationView
┌────────┴────────┐
RootView Push1View
│
PickerView
┌───────┴───────┐
Text1 Text2
PickerView
在这里演示导航不仅可以意味着更改屏幕,还可以意味着更改任何视图的任何状态。
将你的流程描述为一个具有 Step
属性的结构体
@Steps
struct TabSteps {
var tab1
var tab2: SomeTab2Data = .init()
var tab3: NavigationSteps = .screen1
var none
}
@Steps
struct NavigationSteps {
var screen1
var screen2: PickerSteps = .none
}
@Steps
struct PickerSteps {
var text1
var text2
var none
}
var steps: TabSteps = .tab1
如果你想打开 Tab2
,你需要将 tab2
标记为已选择。 你有几种方法可以做到这一点
selected
属性steps.selected = .tab2
steps = .tab2(SomeTab2Data())
你可以检查哪个属性被选中
selected
属性$steps.selected == .tab2
你也可以设置初始选中的属性
var screen3: PickerSteps = .text1
然后你得到了一个深层链接,例如,你需要将 Tab2
更改为具有 NavigationView
的第三个标签,推送到 Push2View
,并在 PickerView
中选择 Text2
。
steps.tab3.$screen2.select(with: .text2)
现在 tab3
、screen3
、text2
属性被标记为已选中。
SwiftUI 是一个状态驱动的框架,因此很容易使用 Step
实现导航。
StateStep
更新视图,存储你的流程结构体,或者从父视图将其绑定为环境变量。 要将流程向下绑定到视图层次结构,你需要使用 .step(...)
或 .stepEnvironment(...)
视图修饰符,或者使用 Binding<Step<...>>
初始化 StateStep
。
stepEnvironment
将当前 step 向下绑定到视图层次结构,用于嵌入的 StateStep
属性。 step
修饰符只是 tag
和 stepEnvironment
修饰符的组合。
struct RootTabView: View {
@StateStep var step: TabSteps = .tab1
var body: some View {
TabView(selection: $step.selected) {
Tab1()
.step(_step.$tab1)
Tab2()
.step(_step.$tab2)
EmbededNavigation()
.step(_step.$tab3)
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .always))
}
}
struct EmbededNavigation: View {
@StateStep var step = NavigationSteps()
var body: some View {
NavigationView {
RootView {
NavigationLink(isActive: $step.isSelected(.screen3)) {
EmbededPicker()
.stepEnvironment($step.$screen2)
} label: {
Text("push")
}
}
}
}
}
struct EmbededPicker: View {
@StateStep var step = PickerSteps()
var body: some View {
Picker("3", selection: $step.selected) {
Text("\(step.prefixString) 0")
.tag(PickerSteps.Steps.text1)
Text("\(step.prefixString) 1")
.tag(PickerSteps.Steps.text2)
}
.pickerStyle(WheelPickerStyle())
}
}
你可以直接使用 Step
,而无需 StateStep
包装器,可以在 ObservableObject
视图模型中,或者作为 TCA Store
中的状态的一部分等等。
对于 UIKit 没有任何特殊的工具,因为 UIKit 不支持状态驱动的导航,但是可以使用 Combine 来订阅 Step
的更改
let stepsSubject = CurrentValueSubject(TabSteps(.tab1))
stepsSubject
.map(\.selected)
.removeDublicates()
.sink { selected in
switch selected {
case .tab1:
...
}
}
stepsSubject.value.$tab2.select()
或者使用 didSet
var steps = TabSteps(.tab1) {
didSet {
guard oldValue.selected != steps.selected else { return }
...
}
}
@StateStep var steps = Steps()
...
NavigationLink(step: _steps.$link) {
...
} label: {
...
}
@StateStep var steps = Steps()
var body: some View {
NavigationStack(path: $steps.navigationPath) {
RootView()
.navigationDestination(step: _steps.$link) {
PushView()
}
// or
.navigationDestination(for: _steps) {
switch $0 {
case .link:
PushView()
.step(_step.$link)
default:
EmptyView()
}
}
}
}
创建一个 Package.swift
文件。
// swift-tools-version:5.9
import PackageDescription
let package = Package(
name: "SomeProject",
dependencies: [
.package(url: "https://github.com/dankinsoid/VDFlow.git", from: "4.31.0")
],
targets: [
.target(name: "SomeProject", dependencies: ["VDFlow"])
]
)
$ swift build
Daniil Voidilov, voidilov@gmail.com
VDFlow 基于 MIT 许可证发布。 详细信息请参见 LICENSE 文件。