非常简单的方式来处理控制器的创建/展示/导航以及降低项目的总体耦合度。
要求
- iOS 8.0+
- macOS 10.10+ (仅限 RxSwift 子规格)
- Xcode 9+
- Swift 4
示例
1.
在应用程序启动时,您可能需要设置主控制器,只需
try? MainTabBarController.presenter().setAsRoot()
2.
然后在某个时候——显示搜索控制器
try? SearchController.presenter().embedInNavigation().present()
3.
然后您想推送用户选择的产品控制器
try? ProductDetailsController.presenter().configure{
$0.product = userSelectedProduct
}.push()
以下示例假设您为每个控制器类型使用单独的故事板。
注意:建议为每个控制器单独创建故事板,这样对于每种控制器类型,您将拥有一个类似下面的单独文件夹
UIViewControllerType.storyboard
UIViewControllerType.swift
UIViewControllerTypeViewModel.swift
UIViewControllerTypeViewModelTests.swift
别忘了在故事板中将您的控制器标记为初始
,这将允许您使用 Presenter 的默认配置。
安装
CocoaPods
pod 'AppRouter'
pod 'AppRouter/Route'
RxSwift 用于 AppRouter 的生命周期可观察扩展:警告: RxSwift subspec 已不再包含 Core
。
pod 'AppRouter'
pod 'AppRouter/RxSwift'
Carthage
github "MLSDev/AppRouter"
内容
易于访问的应用程序
// provides access to keyWindow (also creates one if current is nil)
AppRouter.window
// root controller in current window stack
AppRouter.rootViewController
// topmost controller
AppRouter.topViewController
// returns instance of passed type if its present in tabBar (even if it's embedded in navigationController)
tabBarController.getControllerInstance<T: UIViewController>(_:) -> T?
// returns instance of passed type if its present in navigationController
navigationController.getControllerInstance<T: UIViewController>(_:) -> T?
// returns true if current controller modally presented
viewController.isModal
简单搭建、配置、展示
当然,最好是将控制器创建、配置和展示逻辑从其他控制器和视图中提取出来,放入FlowControllers或者简单的扩展方法(如果您希望这个新控制器工作有效 - 给它们完成块或尝试使用响应式方法)。这将显著减少一致性,并允许您在整个应用程序中只修改一个地方来更改功能背后的逻辑。
extension AppRouter {
static func openGridPictureGalleryControllerWith(pictures: PicturesRepresentation) {
try? GridPictureGalleryController.presenter().fromStoryboard("GridPictureGallery").embedInNavigation().configure{
$0.picturesRepresentation = pictures
}.present()
}
static func openChangePasswordController() {
try? ChangePasswordViewController.presenter().push()
}
}
注意:框架默认使用您的UIViewController子类的名称(String(describing: controllerType))作为Storyboard标识符以及初始控制器(或根ViewController在初始 UINavigationController)
一般来说 - presenter()
返回从其调用的ViewController类相关的配置。这种配置允许您指定如何创建控制器,展示前需要做什么,结果控制器应该在何处展示或推送等。
所有配置方法是文档化的,所以请在使用前阅读。一些可用的方法
指定view controller的来源
func fromStoryboard(_ name: String?, initial : Bool)
- 将尝试从指定的Storyboard创建控制器。
func fromXib(_ name: String?)
- 将尝试从Xib创建。
func from(provider: @escaping () throws -> T)
- 将使用提供的工厂方法创建。
指定目标
func onTop()
- 将使用AppRouter.topViewController作为目标。
func onRoot()
- 将使用AppRouter.rootViewController。
func on(_ provider: @escaping () throws -> UIViewController)
- 将使用提供的控制器。
注意控制器创建、目标解析以及所有其他配置步骤仅在实际调用展示方法(push
、present
、setAsRoot
、show
)时执行
是否应该将控制器嵌入到某个对象中?
func embedInNavigation(_ navigationController: @autoclosure @escaping () -> UINavigationController
- 如果需要从导航控制器中获取控制器。《code>func embedIn(_ embederBlock: @escaping (T) throws -> UIViewController) - 如果需要一些自定义嵌入规则。
在执行显示动作之前进行额外的配置
func configure(_ label: CustomStringConvertible, _ configuration: @escaping (T) throws -> ())
- 可以用来额外配置控制器。
显示控制器
func present()
- 此动作将从您指定的源构建控制器,如果需要,将其嵌入,使用您的额外配置进行配置,解析要显示的目标,然后...显示。
func push()
- 与之前相同,但需要目标控制器本身或在其中(使用ontoViewController.property动来使用)。将推送而不是显示。
func show()
- 全部由您处理(您可以通过handleShow方法来设置它)。
完成处理程序
除了UIKit提供的显示/消失完成块外,AppRouter还提供了您用于在导航堆栈中推送/弹出控制器的完成块。
pushViewController(_, animated:, completion:)
popViewController(animated animated:, completion:)
popToViewController(_:, animated:, completion:)
popToRootViewController(animated animated:, completion:)
popToViewController<T: UIViewController>(_:, animated:, completion:)
如何关闭控制器?
另一个可能的场景:功能控制器可以被推送到应用程序的不同部分。使此类控制器消失(通过关闭按钮等)的简单方法是用关闭方法。只需
@IBAction func closeTapped(sender: UIButton!) {
self.close()
}
就是这样。该方法将尝试检测正确的控制器消失方式。
RxSwift 扩展
如果您想找到一种简单的方式来处理控制器生命周期之外的内容,请尝试使用 AppRouter/RxSwift 子规范。它提供了一组关于生命周期方法的类型和实例可观察对象(它从您订阅的那一刻起使用混入)。
instance.rx.onViewDidLoad() -> Signal<Void>
instance.rx.onViewWillAppear() -> Signal<Bool>
instance.rx.onViewDidAppear() -> Signal<Bool>
instance.rx.onViewWillDisappear() -> Signal<Bool>
instance.rx.onViewDidDisappear() -> Signal<Bool>
Type.rx.onViewDidLoad() -> Signal<Type>
Type.rx.onViewWillAppear() -> Signal<(controller: Type, animated: Bool)>
Type.rx.onViewDidAppear() -> Signal<(controller: Type, animated: Bool)>
Type.rx.onViewWillDisappear() -> Signal<(controller: Type, animated: Bool)>
Type.rx.onViewDidDisappear() -> Signal<(controller: Type, animated: Bool)>
Route 子规范
在 MVVM 世界中使用 Dip、ReusableView 的高级 Presenter.Configuration 被称为 'Route'。它提供了解决 ViewController/ViewModel 的能力,使用 viewModelFactory/viewFactory。
作者
Artem Antihevich,[email protected]
许可证
ReusableView 在 MIT 许可证下可用。有关更多信息,请参阅 LICENSE 文件。