ALCoordinator

此仓库包含一个实现了 Coordinator 模式的库。Coordinator 模式是一种在 iOS 应用开发中用于管理应用导航流程的设计模式。该库提供了一组类和协议,可用于在 iOS 应用中实现 Coordinator 模式。它适用于 UIKit 或 SwiftUI 应用。其核心导航是使用 UINavigationController (UIKit) 创建的,旨在充分利用导航堆栈。


入门指南

要在你的 iOS 项目中使用 Coordinator 模式库,你需要将库文件添加到你的项目并设置一个 Coordinator 对象。以下是基本步骤:


定义 Coordinator

首先,让我们定义我们的路径及其视图。

注意: 如果你想创建一个与 UIKit 兼容的 Coordinator,你必须import UIKCoordinator,否则import SUICoordinator。接下来,我们将编写一个 SwiftUI 的例子。


import SUICoordinator
import SwiftUI

enum OnboardingRoute: NavigationRoute {
  
  case firstStep(viewModel: FirstViewModel)
  case secondStep(viewModel: SecondViewModel)
  
  // MARK: NavigationRouter
  
  var transition: NavigationTransitionStyle {
    switch self {
      case .firstStep:
        return .push
      case .secondStep:
        return .modal
    }
  }
  
  func view() -> any View {
    switch self {
      case .firstStep(let vm):
        return FirstView()
          .environmentObject(vm)
      case .secondStep(let vm):
        return SecondView(viewModel: vm)
    }
  }
}

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

import SUICoordinator

class OnboardingCoordinator: NavigationCoordinator<OnboardingRoute> {

  // MARK: Coordinator
  
  override func start(animated: Bool) {
    let vm = FirstViewModel(coordinator: self)
    router.startFlow(
      route: .firstStep(viewModel: vm),
      animated: animated
    )
  }

  // MARK: Helper funcs
  
  func showStep2() {
    let vm = SecondViewModel(coordinator: self)
    router.navigate(to: .secondStep(viewModel: vm))
  }
  
  func showHomeCoordinator() {
    let coordinator = HomeCoordinatorSUI(currentPage: .settings)
    router.navigate(to: coordinator)
  }
}

创建一个 TabbarCoordinator

1. 创建一个 Router

import SUICoordinator

enum HomeRoute: CaseIterable, TabbarPage {
  
  case marketplace
  case settings
  
  // MARK: NavigationRouter
  
  func coordinator() -> Coordinator {
    switch self {
      case .settings:
        return SettingsCoordinator()
      case .marketplace:
        return MarketplaceCoordinator()
    }
  }
  
  // MARK: TabbarPageDataSource
  
  public var title: String {
    switch self {
      case .marketplace:
        return "Marketplace"
      case .settings:
        return "Settings"
    }
  }
  
  public var icon: Image {
    switch self {
      case .marketplace:
        return Image(systemName: "house")
      case .settings:
        return Image(systemName: "gearshape")
    }
  }
  
  public var position: Int {
    switch self {
      case .marketplace:
        return 0
      case .settings:
        return 1
    }
  }
}

2. 创建一个 TabbarCoordinator

import UIKCoordinator
import UIKit

class HomeCoordinatorUIKit: TabbarCoordinator<HomeRoute> {
  
  // MARK: Constructor
  
  public init() {
    super.init(
      pages: [.marketplace, .settings],
      currentPage: .marketplace
    )
  }
}
import SUICoordinator
import SwiftUI
import Combine

class HomeCoordinatorSUI: TabbarCoordinator<HomeRoute> {

  // MARK: Properties
  
  var cancelables = Set<AnyCancellable>()

  // Custom Tabbar view
  public init(currentPage: HomeRoute) {
    let viewModel = HomeTabbarViewModel()
    let view = HomeTabbarView(viewModel: viewModel)
    viewModel.currentPage = currentPage

    super.init(
      customView: view,
      pages: [.marketplace, .settings],
      currentPage: currentPage
    )
    
    viewModel.$currentPage
      .sink { [weak self] page in
        self?.currentPage = page
      }.store(in: &cancelables)
    
    UITabBar.appearance().isHidden = true
  }
  
  // Default Tabbar view
  public init(default: Bool ) {
    super.init(pages: [.marketplace, .settings])
  }
}

3. 创建 MainCoordinator

import SUICoordinator

class MainCoordinator: NavigationCoordinator<MainRoute> {
  
  // MARK: Constructor
  
  init() {
    super.init(parent: nil)
    router.startFlow(route: .splash, animated: false)
  }
  
  // MARK: Coordinator
  
  override func start(animated: Bool = false) {
    let coordinator = OnboardingCoordinator(presentationStyle: .fullScreen)
    router.navigate(to: coordinator, animated: animated)
  }
}
import SUICoordinator
import SwiftUI

enum MainRoute: NavigationRoute {
  
  case splash

  // MARK: NavigationRoute

  var transition: NavigationTransitionStyle { .push }
  func view() -> any View { SplashScreenView() }
}

设置项目


  1. 如果你的应用支持场景,则创建一个 SceneDelegate 类。
import SwiftUI
import SUICoordinator

final class SceneDelegate: NSObject, UIWindowSceneDelegate {
  
  var mainCoordinator: MainCoordinator?
  var window: UIWindow?
  
  func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    guard let windowScene = (scene as? UIWindowScene) else { return }
    window = UIWindow(windowScene: windowScene)
    setupCoordinator(animated: true)
  }
  
  private func setupCoordinator(animated: Bool = false) {
    mainCoordinator = .init()
    setupWindow(controller: mainCoordinator?.root)
    BaseCoordinator.mainCoordinator = mainCoordinator
    mainCoordinator?.start(animated: animated)
  }
  
  private func setupWindow(controller: UIViewController?) {
    window?.rootViewController = controller
    window?.makeKeyAndVisible()
  }
}

  1. 在你的应用的 AppDelegate 文件中,将 SceneDelegate 类设置为 windowScene 代理。
import UIKit

@main
final class AppDelegate: NSObject, UIApplicationDelegate {
  
  func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
  ) -> Bool {
    return true
  }
  
  func application(
    _ application: UIApplication,
    configurationForConnecting connectingSceneSession: UISceneSession,
    options: UIScene.ConnectionOptions
  ) -> UISceneConfiguration {
    let sessionRole = connectingSceneSession.role
    let sceneConfig = UISceneConfiguration(name: nil, sessionRole: sessionRole)
    sceneConfig.delegateClass = SceneDelegate.self
    return sceneConfig
  }
}

你可以在这里找到一个例子 https://github.com/felilo/TestCoordinatorLibrary


特性

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

Router

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


名称 参数 描述
navigate(_)
  • to: Route,
  • transitionStyle: NavigationTransitionStyle?, default: automatic,
  • animated: Bool?, default true,
  • completion: (() -> Void)?, default: nil
允许你在 Route 中定义的视图之间导航。演示类型有 Push、Modal、ModalFullScreen 和 Custom。
navigate(_)
  • to: Coordinator
  • animated: Bool?, default true,
允许你在 Coordinator 之间导航。它调用 start() 函数。
startFlow(_)
  • to: Route
  • transitionStyle: NavigationTransitionStyle?, default: automatic,
  • animated: Bool?, default true
清除导航堆栈并运行导航流程。
present(_)
  • _ view: ViewType
  • animated: Bool?, default true,
  • completion: (() -> Void)?, default: nil
以模态方式呈现一个视图。
pop(_)
  • animated: Bool?, default true,
从导航堆栈中弹出顶层视图并更新显示。
popToRoot(_)
  • animated: Bool?, default true,
  • completion: (() -> Void)?, default: nil
弹出堆栈上的所有视图,除了根视图,并更新显示。
dismiss(_)
  • animated: Bool?, default true,
关闭视图以模态方式呈现的视图。
popToView(_)
  • _ view: T
  • animated: Bool?, default true,
弹出视图,直到指定的视图位于导航堆栈的顶部。示例:router.popToView(MyView.self)
finishFlow(_)
  • animated: Bool?, default true,
  • completion: (() -> Void)?, default: nil
弹出堆栈上的所有视图,包括根视图,关闭所有模态视图,并从 Coordinator 堆栈中移除当前 Coordinator。

NavigationCoordinator

充当与视图分离的实体,将导航逻辑与演示逻辑分离。这种关注点分离允许视图仅专注于其特定功能,而 Navigation Coordinator 负责应用的整体导航流程。一些功能包括:


名称 参数 描述
router   Route 类型的变量,允许执行 Router 操作。
forcePresentation(_)
  • route: Route
  • transitionStyle: NavigationTransitionStyle?, default: automatic,
  • animated: Bool?, default true,
  • mainCoordinator: Coordinator?, default: mainCoordinator
将当前 Coordinator 放在 Coordinator 堆栈的顶部,使其成为活动和可见的 Coordinator。此功能对于从推送通知、通知中心、非典型流程等启动导航流程非常有用。
getTopCoordinator(_)
  • mainCoordinator: Coordinator?, default mainCoordinator,
返回位于 Coordinator 堆栈顶部的 Coordinator。
restartApp(_)
  • mainCoordinator: Coordinator?, default mainCoordinator,
  • animated: Bool?, default true,
  • completion: (() -> Void)?, default: nil
清除导航堆栈并运行主 Coordinator 导航流程。

TabbarCoordinator

充当与视图分离的实体,将导航逻辑与演示逻辑分离。这种关注点分离允许视图仅专注于其特定功能,而 Navigation Coordinator 负责应用的整体导航流程。它负责构建 Tab bar (UITabbarController),其中包含在其 Route 中定义的 Coordinator,一些功能包括:


名称 参数 描述
currentPage   返回当前选定的页面。
getCoordinatorSelected()
  • mainCoordinator: Coordinator?, default mainCoordinator,
返回与选定选项卡关联的选定 Coordinator。
setPages(_)
  • _values: [PAGE]?, default mainCoordinator,
  • completion: (() -> Void)?, default: nil
更新页面设置。
forcePresentation(_)
  • animated: Bool?, default true,
  • mainCoordinator: Coordinator?, default: mainCoordinator
将当前 Coordinator 放在 Coordinator 堆栈的顶部,使其成为活动和可见的 Coordinator。此功能对于从推送通知、通知中心、非典型流程等启动导航流程非常有用。

安装 💾

SPM

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


贡献

欢迎对 ALCoordinator 库做出贡献!要贡献,只需 fork 此仓库并在新分支中进行更改。准备好更改后,向此仓库提交 pull request 以供审核。

许可证

ALCoordinator 库是在 MIT 许可证下发布的。有关更多信息,请参见 LICENSE 文件。