Swift Compatibility Platform Compatibility codecov

NavigationKit

NavigationKit 是一个库,它扩展了 SwiftUI 对 NavigationStack (仅限 iOS 16 及以上版本) 的实现,并添加了更多资源来管理用户界面。

安装

本仓库通过 SPM 分发,可以通过两种方式使用

  1. Xcode

在 Xcode 14 中,前往 File > Packages > Add Package Dependency...,然后粘贴 https://github.com/brennobemoura/navigation-kit.git

  1. Package.swift
// swift-tools-version: 5.7
import PackageDescription

let package = Package(
    name: "MyPackage",
    products: [
        .library(
            name: "MyPackage",
            targets: ["MyPackage"]
        )
    ],
    dependencies: [
        .package(url: "https://github.com/brennobemoura/navigation-kit.git", from: "1.0.0")
    ],
    targets: [
        .target(
            name: "MyPackage",
            dependencies: ["NavigationKit"]
        )
    ]
)

用法

可用的主要功能如下所列。对于每个功能,都有一个问题解决方案,开发该方案的目的是为了解决耦合的 SwiftUI 视图问题。

NKNavigationStack

struct ContentView: View {

    var body: some View {
        NKNavigationStack {
            FirstView()
        }
    }
}

使用 NKNavigationStack 将 NavigationPath 替换为 NavigationAction,这使得开发者能够更好地操作当前堆叠的视图。

⚠️此实现的缺点是移除了 Apple 提供的 Decode 选项。但是您仍然可以使用 NavigationPath 实现您自己的 NavigationStack 版本,并在没有 NavigationAction 环境的情况下使用 NavigationKit。

struct FirstView: View {

    @Environment(\.navigationAction) var navigationAction
    
    var body: some View {
        Button("Push") {
            // SomeModel needs to be mapped using
            // navigationDestination(for:destination:)
            // using SwiftUI's method.
            navigationAction.append(SomeModel())
        }
    }
}

ViewResolver

struct FirstView: View {

    @Environment(\.viewResolver) var viewResolver
    
    var body: some View {
        // SomeModel needs to be mapped using
        // viewResolver(for:_:) {}.
        viewResolver(SomeModel())
    }
}

为了将模型与相应的视图进行映射,可以使用 viewResolver(for:_:) 方法,需要在使用前在一个视图中指定。

struct ContentView: View {

    var body: some View {
        NKNavigationStack {
            FirstView()
                .viewResolver(for: SomeModel.self) {
                    SecondView($0)
                }
        }
    }
}

SceneAction

struct FirstView: View {

    @Environment(\.sceneAction) var sceneAction
    
    var body: some View {
        Button("Push") {
            // SomeModel needs to be mapped using
            // sceneAction(for:perform:) and
            // the sceneActionEnabled() called in the root
            // hierarchy.
            sceneAction(SomeModel())
        }
    }
}

为了映射 action,需要调用 sceneAction(for:perform:) 方法,该方法将捕获在任何可能监听的地方抛出的 action。

⚠️只有在之前调用 sceneActionEnabled() 方法后,SceneAction 环境才可用。

struct ContentView: View {

    var body: some View {
        NKNavigationStack {
            FirstView()
        }
        .sceneAction(for: SomeModel.self) {
            print("Action received: \($0)")
        }
        // but, sceneActionEnabled is needed before 
        // somewhere in the application
        .sceneActionEnabled()
    }
}

建议:在 App 的 body 属性中调用 sceneActionEnabled()

ViewModelConnection

ViewModelConnection 使得可以将 ViewModel 连接到 View,同时保持 SwiftUI State 同步和更新。

此实现旨在在 Coordinator 结构体内部使用。

struct SecondCoordinator: View {

    let model: SomeModel
    
    var body: some View {
        ViewModelConnection(model, SecondViewModel.init) { viewModel in
            SecondView(viewModel: viewModel)
        }
    }
}

为了按照 Coordinator 的设计意图管理流程,您需要为 ViewModel 指定 destination 属性,以便您可以像这样工作

struct SecondCoordinator: View {

    let model: SomeModel
    
    var body: some View {
        ViewModelConnection(model, SecondViewModel.init) { viewModel in
            SecondView(viewModel: viewModel)
                .onReceive(viewModel.$destination) { destination in
                    switch destination {
                    case .error(let error):
                        errorScene(error)
                    case .third(let third):
                        thirdScene(third)
                    case .none:
                        break
                    }
                }
        }
    }
}

errorScenethirdScene 内部,您可以调用 navigationActionsceneAction 来执行某些操作。