恢复 0.6.6

恢复 0.6.6

Connor Neville维护。



恢复 0.6.6

  • 作者
  • Connor Neville

恢复

Version License Platform

恢复是一个用于使用视图控制器层级创建应用的工具箱,有助于消解一个UIViewController必须在应用中对应一个单屏的神话。使用容器和子视图控制器编写干净且易于理解的代码逻辑,保持每个组件的大小,使其更容易维护。

UIKit自带一些内置的容器视图控制器,旨在协调应用逻辑(例如UINavigationControllerUITabBarControllerUIPageViewController等)。它们持有其中一个或多个子视图控制器,并处理在它们之间切换所需的应用逻辑。恢复旨在使您自己的UIViewControllers中轻松使用此模式变得更容易,帮助将应用分解为可组合的部分。您从这种模式中获得了什么?

视图控制器组合的优势

  • 您的视图控制器相互依存度降低。您的视图控制器是否做些像self.navigationController?.pushViewController(...这样的操作?这是常见的,但现在这个视图控制器不知道如何在外部操作特定的导航堆栈。
  • 这种模式可以扩展到单个屏幕。如果您有一个非常复杂的UI,而不是一个单独的、庞大的视图控制器,您可以考虑使用单个容器视图控制器来管理一些子视图控制器。
  • 更容易识别错误,更有信心回归不会发生。回到上面的例子,假设您在非常复杂的UI中的一个小部件里有一个错误。作为一个单独的视图控制器,您可能不知道您的更改是否会影响到其他地方的视图控制器,甚至可能更难定位错误。但是,使用容器和子视图控制器,您可以隔离特定的组件并确保其他组件没有参与。
  • 与拆分成多个 UIView 的屏幕不同,每个子 UIViewController 都能享受到 UIViewController 生命周期事件的好处。你可以在 viewWillAppear 中提供特定的逻辑,只在屏幕的一个组件上执行。或者,当单个组件旋转到横屏模式时,你可以自定义其布局代码。

如何入门?

安装

Reinstate 通过 CocoaPods 提供。要安装它,只需将以下行添加到 Podfile 中

pod 'Reinstate'

添加和移除子视图控制器

添加 UIView 子类到 UIViewController 可能成了你的第二天性。但是,如何添加一个子 UIViewController 呢?不使用 Reinstate 的话看起来是这样

// childController is the new child view controller
self.addChildViewController(childController)
view.addSubview(childController.view)
NSLayoutConstraint.activate([
    childController.view.topAnchor.constraint(equalTo: view.topAnchor),
    childController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
    childController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
    childController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
childController.didMove(toParentViewController: self)

如果想要动画化这种变化,事情会变得更加复杂。然而,使用 Reinstate

self.addChild(childController)
// or, an animated version:
self.addChild(childController, animations: (duration: 0.3, options: .transitionCrossDissolve))
// or, add it to a subview instead of self.view:
self.addChild(childController, constrainedTo: containerView, animations: (duration: 0.3, options: .transitionCrossDissolve))

你可以以类似的方式添加和替换子视图控制器。但是,Reinstate 不仅限于添加和移除这些子视图控制器:它还提供了几个干净的可视控制器,你可以用它来管理你的应用程序的状态。

StatefulViewController

StatefulViewController 是一个子类化的 UIViewController,它可以根据一个 State 来管理其内容。它是通过交换你可以映射到控制器状态的子视图控制器来实现的。StatefulViewController 可以大大减少混淆性代码,并为你提供了一个简单、易于阅读的 API 来指定过渡动画和设置。

import Reinstate

enum RootViewState {
    case splash
    case onboarding
    case signIn
    case home
}

class RootViewController: StatefulViewController<RootViewState> {

    var currentChild: UIViewController?

    override func childViewController(for state: RootState) -> UIViewController {
        switch state {
        case .splash:
            let vc = SplashViewController()
            vc.delegate = self
            return vc
        case .onboarding:
            let vc = OnboardingViewController()
            vc.delegate = self
            return vc
        case .signIn:
            let vc = SignInViewController()
            vc.delegate = self
            return vc
        case .home:
            let vc = HomeViewController()
            vc.delegate = self
            return vc
        }
    }

    override func transitionAnimation(from oldState: RootState, to newState: RootState) -> StateTransitionAnimation? {
        switch (oldState, newState) {
        case (.splash, _):
            return .appearAndSimultaneouslyRemove(
                onAppear: (0.3, .transitionCrossDissolve),
                onRemove: (0.3, .transitionCrossDissolve))
        case (.onboarding, .signIn), (.signIn, .home):
            return .appearOverPrevious(
                onAppear: (0.3, .transitionFlipFromLeft))
        case (.signIn, .onboarding), (.home, .signIn):
            return .appearUnderPrevious(
                onRemove: (0.3, .transitionFlipFromRight))
        default:
            return nil
        }
    }

}

extension RootViewController: SplashViewControllerDelegate {

    func splashViewControllerDidComplete(_ controller: SplashViewController) {
        switch (UserDefaults.standard.isAuthenticated, UserDefaults.standard.hasCompletedOnboarding) {
        case (true, _):
            transition(to: .home, animated: true)
        case (false, true):
            transition(to: .signIn, animated: true)
        case (false, false):
            transition(to: .onboarding, animated: true)
        }
    }

}

extension RootViewController: OnboardingViewControllerDelegate {

    func onboardingViewControllerDidComplete(_ controller: OnboardingViewController) {
        UserDefaults.standard.hasCompletedOnboarding = true
        transition(to: .signIn, animated: true)
    }

}

// Other delegate implementations omitted for brevity

StatefulNavigationController

StatefulNavigationController 是一个围绕 UINavigationController 的包装器,它可以根据其 NavigationState 来管理其导航堆栈。与 StatefulViewController 类似,你告诉它为哪个状态创建哪个视图控制器,并告诉它在何时过渡。API 的目的是与 StatefulViewController 相似。请参见示例项目以了解用法。

流程图描述了你的过渡是推送还是删除

StatefulTabBarController

StatefulNavigationControllerUITabBarController 的包装器,与其他状态控制器类似。其 API 高度类似于其他两个(一个区别:Tab Bar 需要在初始化时知道所有可能的状态)。请参考示例项目了解使用方法。

示例

要运行示例项目,克隆仓库,打开并运行 Example/Reinstate.xcworkspace

作者

Connor Neville, [email protected]

许可

Reinstate 可在 MIT 许可下使用。有关更多信息,请参阅 LICENSE 文件。