Skip to content

Latest commit

 

History

History
138 lines (106 loc) · 4.95 KB

README_CN.md

File metadata and controls

138 lines (106 loc) · 4.95 KB

SwiftUINavigator

CI License: MIT

为SwiftUI应用提供的一个声明式的路由和导航框架。

注意: 这是一个实验性的框架,生产环境使用需要仔细评估。

注意2: iOS16推出的NavigationStack比起NavigationView已经优化了非常多,如果你的应用只需要支持iOS16及以上,建议优先考虑NavigationStack

特点

优点:

  • 中心化、声明式的路由定义
  • 更简单易用的页面跳转接口
  • 支持push/present
  • 支持replace
  • 支持多级返回
  • 支持自定义转场动画
  • 页面间无耦合
  • 基于字符串的sceneName跳转,更灵活,方便配置化
  • 纯SwiftUI实现

缺点:

  • 使用ZStack自定义的页面栈,存在一些限制:
    1. 不兼容原生导航栈(NavigationView
    2. 需要自定义导航栏
    3. 不兼容原生Transition,自定义动画需要自行插值计算,比较麻烦(当页面中存在List时跟Transition有冲突)
    4. 其它未知问题

动机

SwiftUI是一个优秀的声明式UI框架,但官方提供的NavigationView是个非常僵硬的命令式路由系统,起始页需要完整构造出落地页的View才能进行跳转,页面间耦合太重,并且缺少很多能力,并不足以支持一个中大型应用的开发。

因此我设计了SwiftUINavigator,希望提供一个更契合SwiftUI这种声明式UI思想的路由框架,并提供更加完善的导航能力。然而由于SwiftUI的能力限制,最终实现完全抛弃了原生导航,使用了基于ZStack的自定义页面栈方案,此方案的可靠性目前还有待验证。

例子

这里提供了一些例子来演示如何利用SwiftUINavigator来提供路由能力。

CaseStudy

Simulator.Screen.Recording.-.iPhone.13.-.2022-10-07.at.21.26.39.mp4

使用

首先,声明路由。

  • Router是此路由框架的根节点,它会初始化所需的路由环境。通常情况下Router应该在一个应用的最外层。
  • NavScene代表一个页面,只有当前页面的name与其匹配时才会渲染其内容
@main
struct CaseStudyApp: App {
    var body: some Scene {
        WindowGroup {
            Router(initialName: HomeSceneName) {
                NavScene(HomeSceneName) {
                    HomeScene()
                }
                NavScene(NavActionSceneName) { properties in
                    let tagStr = properties["tag"] ?? "0"
                    NavActionScene(tag: Int(tagStr) ?? 0)
                }
                NavScene(FadeSceneName) {
                    FadeScene()
                }.fadeTransition()
                NavScene(PopupSceneName) {
                    PopupScene()
                }
                NavScene("*") {
                    UnknownScene()
                }
            }
        }
    }
}

之后,开发scene。scene中可以获取到3个EnvironmentObject:

  • Navigator : 路由控制器,通过Navigator控制页面的进出等
  • NavigationState : 路由状态,通过NavigationState可以访问完整的页面栈
  • NavigateSceneState : 页面状态,通过NavigateSceneState可以访问当前页面的状态
let NavActionSceneName = "nav_action"
struct NavActionScene: View {
    @EnvironmentObject private var navigator: Navigator
    @EnvironmentObject private var navigationState : NavigationState
    @EnvironmentObject private var sceneState : NavigateSceneState
    var tag : Int = 0
    var body: some View {
        var fullPath = ""
        for sceneState in navigationState.historyStack {
            fullPath += "/\(sceneState.sceneName)"
        }
        return VStack() {
            NavBar(title: "NavAction-\(tag)")
            Text("currentFullPath:  \(fullPath)")
            
            Button("push") {
                navigator.push(NavActionSceneName,properties: ["tag": String(tag + 1)])
            }
            Button("present") {
                navigator.present(NavActionSceneName,properties: ["tag": String(tag + 1)])
            }
            Button("replace") {
                navigator.replace(NavActionSceneName,properties: ["tag": String(tag + 1)])
            }
            Button("backToHome") {
                navigator.goBack(total: .max, animated: true)
            }
            Spacer()
        }.background(sceneState.navigationType == .Push ? .white : .gray)
            .offset(x: 0, y: sceneState.navigationType == .Push ? 0 : 150)
    }
}

更详细的使用方式请参考例子

依赖

  • iOS 14.0+ / macOS 12.0+ / tvOS 14.0+ / watchOS 8.0+

安装

You can add SwiftUINavigator to an Xcode project by adding it as a package dependency.

https://github.com/javanli/SwiftUI-Navigator

其他

欢迎提Issue和PR,也可以通过邮箱(javanli@qq.com)联系我。