SUICoordinator

此仓库包含一个库,该库实现了 Coordinator 模式。Coordinator 模式是一种在 iOS 应用程序开发中用于管理应用程序导航流程的设计模式。该库提供了一组可用于实现 Coordinator 流程的功能。此库不使用任何 UIKit 组件。


入门指南

要在您的 iOS 项目中使用 SUICoordinator 库,您需要将库文件添加到您的项目中。以下是基本步骤:


定义 Coordinator

首先,让我们定义路径和视图。


import SwiftUI
import SUICoordinator

enum HomeRoute: RouteType {
    
    case push(viewModel: PushViewModel)
    case sheet(viewModel: SheetViewModel)
    case fullscreen(viewModel: FullscreenViewModel)
    case detents(viewModel: DetentsViewModel)
    case actionListView(viewModel: ActionListViewModel)
    
    var presentationStyle: TransitionPresentationStyle {
        switch self {
            case .push:
                return .push
            case .sheet:
                return .sheet
            case .fullscreen:
                return .fullScreenCover
            case .detents:
                return .detents([.medium])
            case .actionListView:
                return .push
        }
    }

    @ViewBuilder
    var view: Body {
        switch self {
            case .push(let viewModel):
                PushView(viewModel: viewModel)
            case .sheet(let viewModel):
                SheetView(viewModel: viewModel)
            case .fullscreen(let viewModel):
                FullscreenView(viewModel: viewModel)
            case .detents(let viewModel):
                DetentsView(viewModel: viewModel)
            case .actionListView(let viewModel):
                NavigationActionListView(viewModel: viewModel)
        }
    }
}

其次,让我们创建第一个 Coordinator。 所有 Coordinator 都应该实现 start() 函数,然后启动流程(强制性)。 最后,添加额外的流程。

import SUICoordinator

class HomeCoordinator: Coordinator<HomeRoute> {
    
    override func start(animated: Bool = true) async {
        let viewModel = ActionListViewModel(coordinator: self)
        await startFlow(route: .actionListView(viewModel: viewModel), animated: animated)
    }
    
    func navigateToPushView() async {
        let viewModel = PushViewModel(coordinator: self)
        await router.navigate(to: .push(viewModel: viewModel))
    }
    
    func presentSheet() async {
        let viewModel = SheetViewModel(coordinator: self)
        await router.navigate(to: .sheet(viewModel: viewModel))
    }
    
    func presentFullscreen() async {
        let viewModel = FullscreenViewModel(coordinator: self)
        await router.navigate(to: .fullscreen(viewModel: viewModel))
    }
    
    func presentDetents() async {
        let viewModel = DetentsViewModel(coordinator: self)
        await router.navigate(to: .detents(viewModel: viewModel))
    }
    
    func presentTabbarCoordinator() async {
        let coordinator = CustomTabbarCoordinator()
        await navigate(to: coordinator, presentationStyle: .sheet)
    }
    
    func close() async {
        await router.close()
    }
    
    func finsh() async {
        await finishFlow(animated: true)
    }
}

然后,让我们创建一个 View 及其 ViewModel。

import Foundation

class ActionListViewModel: ObservableObject {
    
    let coordinator: HomeCoordinator
    
    init(coordinator: HomeCoordinator) {
        self.coordinator = coordinator
    }
    
    @MainActor func navigateToFirstView() async {
        await coordinator.navigateToPushView()
    }
    
    @MainActor func presentSheet() async {
        await coordinator.presentSheet()
    }
    
    @MainActor func presentFullscreen() async {
        await coordinator.presentFullscreen()
    }
    
    @MainActor func presentDetents() async {
        await coordinator.presentDetents()
    }
    
    @MainActor func presentTabbarCoordinator() async {
        await coordinator.presentTabbarCoordinator()
    }
    
    @MainActor func finish() async {
        await coordinator.finish()
    }
}
import SwiftUI

struct NavigationActionListView: View {
    
    typealias ViewModel = ActionListViewModel
    @StateObject var viewModel: ViewModel
    
    var body: some View {
        List {
            Button("Push NavigationView") {
                Task { await viewModel.navigateToPushView() }
            }
            
            Button("Presents SheetView") {
                Task { await viewModel.presentSheet() }
            }
            
            Button("Presents FullscreenView") {
                Task { await viewModel.presentFullscreen() }
            }
            
            Button("Presents DetentsView") {
                Task { await viewModel.presentDetents() }
            }
            
            Button("Presents Tabbar Coordinator") {
                Task { await viewModel.presentTabbarCoordinator() }
            }
        }
        .navigationTitle("Navigation Action List")
    }
}

设置项目


  1. 创建一个 AppDelegate 类并执行以下实现,或者如果您喜欢,跳过此步骤并在下一步中执行相同的实现。
import SwiftUI
import SUICoordinator

class AppDelegate: NSObject, UIApplicationDelegate {
    
    var mainCoodinator: HomeCoordinator?
    
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil
    ) -> Bool {
        mainCoodinator = HomeCoordinator()


        // Simulate the receipt of a notification or external trigger to present some coordinator
        DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
            Task { [weak self] in
                // Create and present the CustomTabbarCoordinator in a sheet presentation style
                let coordinator = CustomTabbarCoordinator()
                try? await coordinator.forcePresentation(
                    presentationStyle: .sheet,
                    mainCoordinator: self
                )
            }
        }


        return true
    }
}

  1. 在 App 文件中,按照以下实现操作
import SwiftUI

@main
struct SUICoordinatorDemoApp: App {
    
    @UIApplicationDelegateAdaptor private var appDelegate: AppDelegate
    
    var body: some Scene {
        WindowGroup {
            appDelegate.mainCoodinator?.getView()
        }
    }
}

示例项目

为了更好地理解,我建议您查看位于 Example 文件夹中的示例项目。

example_.mov

特性

以下是您可以执行的最重要的特性和操作:

RouteType

要在 SUICoordinator 中创建任何路由,您需要将您的对象扩展到 RouteType 协议; 此外,如果需要,您可以添加自定义函数。 正如您所看到的,在我们的示例中,我们使用自定义类型 (enums) 来实现它。

最后但并非最不重要的一点是,您还可以使用 DefaultRoute 创建自定义路由,如 TabBarFlowCoordinator 示例中所示

Router

Router 负责管理导航堆栈并协调不同视图之间的转换。 它从视图中抽象出导航细节,使它们能够专注于其特定功能,例如:


名称 参数 描述
navigate(_)
  • to: Route,
  • presentationStyle: TransitionPresentationStyle?, default: nil,
  • animated: Bool?, default true,
是一个异步函数,允许您在 Route 中定义的视图之间导航。 presentation 的类型包括 Push、Sheet、Fullscreen 和 Detents
present(_)
  • _ view: ViewType
  • presentationStyle: TransitionPresentationStyle?, default: nil,
  • animated: Bool?, default true,
是一个异步函数,呈现一个视图,例如 Sheet、Fullscreen 或 Detents
pop(_)
  • animated: Bool?, default true,
是一个异步函数,从导航堆栈中弹出顶部视图并更新显示。
popToRoot(_)
  • animated: Bool?, default true,
是一个异步函数,弹出堆栈上的所有视图,除了根视图并更新显示。
dismiss(_)
  • animated: Bool?, default true,
是一个异步函数,关闭视图以模态方式呈现的视图。
popToView(_)
  • _ view: T
  • animated: Bool?, default true,
是一个异步函数,弹出视图,直到指定的视图位于导航堆栈的顶部。 示例:router.popToView(MyView.self)
close(_)
  • animated: Bool?, default true,
是一个异步函数,关闭或弹出在 Coordinator 中呈现的最后一个视图。

Coordinator

作为视图的单独实体,将导航逻辑与呈现逻辑分离。 这种关注点分离使视图能够专注于其特定功能,而 Navigation Coordinator 负责应用程序的整体导航流程。 一些特性是:


名称 参数 描述
router Route 类型的变量,允许执行 router 操作。
startFlow(_)
  • to: Route
  • transitionStyle: TransitionPresentationStyle?, default: automatic,
  • animated: Bool?, default true
是一个异步函数,清除导航堆栈并运行导航流程。
finishFlow(_)
  • animated: Bool?, default true,
是一个异步函数,弹出堆栈上的所有视图,包括根视图,关闭所有模态视图,并从 coordinator 堆栈中删除当前的 coordinator。
forcePresentation(_)
  • route: Route
  • presentationStyle: TransitionPresentationStyle?, default: automatic,
  • animated: Bool?, default true,
  • mainCoordinator: Coordinator?, default: mainCoordinator
是一个异步函数,将当前 coordinator 放在 coordinator 堆栈的顶部,使其成为活动和可见的 coordinator。 此功能对于从推送通知、通知中心、非典型流程等启动导航流程非常有用。
navigate(_)
  • to: Coordinator
  • presentationStyle: TransitionPresentationStyle?, default: automatic,
  • animated: Bool?, default true,
是一个异步函数,允许您在 Coordinator 之间导航。 它调用 start() 函数。
finishFlow(_)
  • animated: Bool?, default true,
是一个异步函数,弹出堆栈上的所有视图,包括根视图,关闭所有模态视图,并从 coordinator 堆栈中删除当前的 coordinator。

TabbarCoordinator

它的工作方式与 Coordinator 相同,但具有以下附加功能:


名称 参数 描述
currentPage Page 类型的变量,允许设置和获取所选的标签
getCoordinatorSelected()
  • mainCoordinator: Coordinator?, default mainCoordinator,
返回与所选标签关联的选定的 coordinator
setPages(_)
  • _values: [PAGE]?, default mainCoordinator,
是一个异步函数,更新页面集。
getCoordinator(_)
  • position: Int
返回给定位置的 coordinator 作为参数
setBadge
  • PassthroughSubject: (String?, Page)?
允许设置标签的徽章的变量
customView
  • view: View
是一个接收视图作为参数的闭包,用于创建自定义标签栏

安装 💾

SPM

打开 Xcode 和你的项目,点击 File / Swift Packages / Add package dependency...。 在文本框 "Enter package repository URL" 中,写入 https://github.com/felilo/SUICoordinator 并按两次 Next


贡献

欢迎对 SUICoordinator 库做出贡献! 要做出贡献,只需 fork 此存储库并在新分支中进行更改。 当您的更改准备好后,提交拉取请求到此存储库以供审核。