Marshroute
内容
概述
Marshroute
是一个库,它将鼓励你无论你更喜欢哪种架构,都将所有导航逻辑放在 Router
层。 Marshroute
有助于使你的 Router
语法简洁明了。
主要功能
- 每个由
Router
驱动的转换都会转发到顶部最层的UIViewController
。这意味着你可以在程序的任何位置让Marshroute
显示一个视图控制器,它将简单地工作! - 无论你的模块是如何被展示的,你可以简单地让你的模块的
Router
通过调用router.dismissCurrentModule()
来解散这个模块,并且它将简单地工作!你的父模块可以在未来改变展示风格(例如,以模态方式展示而不是推动),但router.dismissCurrentModule()
仍然会工作! - 无论你的模块如何展示后续模块,你可以简单地让你的模块的
Router
通过调用router.focusOnCurrentModule()
回到这个模块,并且它也会简单地工作! Marshroute
可以通过一行代码更改转换动画(更多详情请见调整转换动画)Marshroute
支持3d触摸转换Marshroute
可以检测视图控制器的保留周期,并通过断言API通知你。你可以通过你的实现来覆盖默认断言:例如,将断言打印到输出或进行一些高级分析(更多详情请见插件自定义)Marshroute
提供了一个详细的演示项目,其中描述了iPhone和iPad上的关键导航原理
详情
每个Router
驱动的转换都会转发到顶部最层的UIViewController
,这使得它超级容易支持DeepLink
,例如从应用的任何位置展示Authorization
模块。我更喜欢直接从我的根应用的Router
来做这件事。
这个仓库允许你以非常干净、描述性和灵活的方式驱动你的转换。例如,以下代码似乎是从你的根应用的Router
中提取的:
func showAuthorization() {
pushViewControllerDerivedFrom { routerSeed -> UIViewController in
let authorizationAssembly = assemblyFactory.authorizationAssembly()
let viewController = authorizationAssembly.module(
routerSeed: routerSeed
)
return viewController
}
}
此代码将一个Authorization
视图控制器推送到最顶层的UINavigationController
栈中。只有当它是为Authorization
模块创建一个Router
时才会使用到routerSeed
参数。
这里的魔法就在这一行代码中
pushViewControllerDerivedFrom { routerSeed -> UIViewController in
如果你简单地想要改变展示风格以支持模态转换,只需将其更改为
presentModalNavigationControllerWithRootViewControllerDerivedFrom { routerSeed -> UIViewController in
如果你出于某些原因不需要为你的Authorization
模块一个UINavigationController
,你可以通过以下方式实现
presentModalViewControllerDerivedFrom { routerSeed -> UIViewController in
再次,转换将转发到顶部,让Router
保持非常简洁和直接。因此,Router
只负责一件事情:选择转换的风格。
调整过渡动画
您可以通过添加动画器来自定义过渡的样式。例如
func showCategories() {
presentModalNavigationControllerWithRootViewControllerDerivedFrom( { routerSeed -> UIViewController in
let categoriesAssembly = assemblyFactory.categoriesAssembly()
let viewController = categoriesAssembly.module(
routerSeed: routerSeed
)
return viewController
}, animator: RecursionAnimator())
}
这里的关键行是
}, animator: RecursionAnimator())
所以语法保持清洁,切换回原始动画样式也超级容易。
3d触摸支持
PeekAndPopUtility
想要添加花哨的窥视和弹出预览?易如反掌!只需使用PeekAndPopUtility
从MarshrouteStack
中,并注册您的视图控制器作为可以预览其他控制器的!
peekAndPopUtility.register(
viewController: self,
forPreviewingInSourceView: peekSourceView,
onPeek: { [weak self] (previewingContext, location) in
self?.startPeekWith(
previewingContext: previewingContext,
location: location
)
},
onPreviewingContextChange: nil
)
peekSourceView
用于在预览动画期间由UIKit
捕获屏幕截图。您可以将单个视图控制器注册为在多个源视图中预览(例如:在表格视图中和在导航栏中)。
onPeek
闭包将在peekSourceView
中发生强触控手势时被调用。在您的startPeekWith(previewingContext:location:)
方法中,您应该执行以下操作:
-
找到用户与之交互的视图(交互视图)。您应该在
previewingContext.sourceView
的坐标系中使用指定的location
。 -
调整
previewingContext
的sourceRect
。您应该将那个交互视图的帧转换到previewingContext.sourceView
的坐标系。UIKit
使用sourceRect
在用户按下时保持其视觉效果,同时在周围内容上应用模糊。 -
调用过渡,这在用户简单地点击同一
location
时通常发生。例如,如果用户按下一个UIControl
,您可以调用sendActions(for: .touchUpInside)
来调用该UIControl
的动作处理程序。
假设上述提到的行为处理器最终导致某些路由器调用pushViewControllerDerivedFrom(_:)
来推入一个新的视图控制器。在这种情况下,实际上不会发生推送。取而代之的是,Marshroute
将冻结转换并预览目标视图控制器。只有当用户提交预览(即退出预览)时,转换才会最终执行。
上述描述的行为仅在活动peek
请求期间发生(当UIViewControllerPreviewingDelegate
请求预览视图控制器时)。在所有其他情况下,pushViewControllerDerivedFrom(_:)
将立即推入,如预期的那样。
重要提示:如果您在onPeek
闭包中没有调用任何转换,或者调用异步转换,则不会发生预览。这种行为是由于UIKit
Api的限制:UIViewControllerPreviewingDelegate
需要同步返回预览视图控制器。
您还可以使用onPreviewingContextChange
闭包来设置您的手势识别器失败关系。
预览和移除状态观察
您可以使用来自MarshrouteStack
的PeekAndPopStateObservable
来观察任何视图控制器的peek
和pop
状态变化。这可能对分析目的有用。
peekAndPopStateObservable.addObserver(
disposable: self,
onPeekAndPopStateChange: { viewController, peekAndPopState in
debugPrint("viewController: \(viewController) changed `peek and pop` state: \(peekAndPopState)")
}
)
您还可以使用PeekAndPopStateViewControllerObservable
来观察您特定的视图控制器的peek
和pop
状态变化。这可能有助于在peek
和popped
模式下调整视图控制器的外观。
peekAndPopStateViewControllerObservable.addObserver(
disposableViewController: self,
onPeekAndPopStateChange: { [weak self] peekAndPopState in
switch peekAndPopState {
case .inPeek:
self?.onPeek?()
case .popped:
self?.onPop?()
case .interrupted:
break
}
}
)
在这里,在onPeek
和onPop
闭包中,您的Presenter
可以强制视图根据其UI进行更新。
view?.onPeek = { [weak self] in
self?.view?.setSimilarSearchResultsHidden(true)
}
view?.onPop = { [weak self] in
self?.view?.setSimilarSearchResultsHidden(false)
}
演示
查看演示
项目。这个演示是用Swift
编写的,使用VIPER
架构,显示了Router
现在具备的所有功能。
在模拟器上运行此演示,并检查模拟内存警告或设备震动时会发生什么。您将看到由根模块的Router
(即UITabBarController
的Router
)驱动的几种类型的转换。
此演示项目针对iPhone
和iPad
,并为每个受支持的设备 intentions 创建独特的Router
实现,从而强调了将导航逻辑从View
层移动到Router
层的价值。
当您轻触一个蓝色计时器图块时,您将为此图块所属的模块安排一个反向转换。为了看到这种效果,您应该在导航堆栈中进行多次转换。
从0.4.0版本开始,演示项目更新为显示PeekAndPopUtility
的实际操作:您可以通过按下任何表格视图单元格或导航栏按钮来预览底层转换。您还可以了解如何使用PeekAndPopStateViewControllerObservable
来调整AdvertisementViewController
在peek
和popped
模式下的外观:在peek
模式下,您将只看到一个全屏的彩色图像图案,而在popped
模式下,您还将看到一个类似的广告部分。
要求
- iOS 9.0+
- Xcode 14.0+
注意:peek和pop仅支持iOS 9.0+
安装
Cocoapods
要使用CocoaPods安装Marshroute,请在您的Podfile
中添加以下行
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
use_frameworks!
pod 'Marshroute'
然后运行pod install
命令。有关CocoaPods安装和使用详情,请访问其官方网站。
Carthage
要使用Carthage安装Marshroute,请将以下行添加到您的Cartfile
github "avito-tech/Marshroute" ~> 1.0.0
然后运行 carthage update --platform iOS
命令。有关 Carthage 的安装和使用详情,请访问 其仓库网站。
自定义
您可以使用 MarshroutePrintPlugin
和 MarshrouteAssertionPlugin
提供自定义打印和断言实现。这就像这样
MarshroutePrintManager.setUpPrintPlugin(YourPrintPlugin())
MarshrouteAssertionManager.setUpAssertionPlugin(YourAssertionPlugin())
许可证
MIT
Objective-c 支持
该框架是用纯 Swift
编写的,使用了其最新的功能,因此如果您想在您的 Objective-c
应用中使用 Marshroute
,您将不得不用 Swift
编写您的 Router
。
有用链接
您可以观看 此视频 了解形成 Marshroute
基础的原因和想法(俄语)。
您还可以阅读 这篇指南,了解如何在您的应用中实现 DeepLink
支持时使用 Marshroute
。
作者
Timur Yusipov ([email protected], [email protected], https://twitter.com/Fizmatchel, https://stackoverflow.com/users/2982854/tim).