plasticbrain
plasticbraindotcom/SnapNavigationGitHub存储库
SnapNavigation
是iOS的可组合视图导航
SnapNavigation提供了一种全面的方法来定义和处理所有iOS应用程序视图导航问题。
SnapNavigation允许您将导航代码分离成专用对象,使您能够以您认为合适的方式组合每个方面:动态确定源视图和目标视图,协调角色之间,以及执行表现代码。它适用于从代码触发的以及从Storyboard动作联接触发的事件。
快速入门
import UIKit
// Basic SnapNavigator with no internal navigation logic, just default implementations.
class MyNavigator: SnapNavigator {}
// Some particular view controller in your application.
class MyViewController: UIViewController {
// Create a SnapNavigator that uses only the default implementation methods.
let navigator = MyNavigator()
var someData: String = "Sample data"
// Example navigations.
func composableNavigationExamples() {
// Note: Some examples show internal creation of a destination view controller. This is not good practice unless the destination is intended as an internally controlled child view controller.
// Composable navigation example 1:
// Create a secondary view controller, and show it.
let destinationVC = UIViewController()
navigator.navigate(from: .viewController(self), to: .viewController(destinationVC), with: .show)
// Composable navigation example 2:
// Create a secondary view controller, pass a value to it via closure and show it.
let destinationWithValueVC = MyViewController()
let mediation2: (UIViewController, UIViewController) -> () = { source, destination in
if let destination = destination as? MyViewController {
destination.someData = "Sent message"
}
}
navigator.navigate(from: .viewController(self), to: .viewController(destinationWithValueVC), applying: .method(mediation2), with: .show)
// Composable navigation example 3:
// Navigation using a set navigation model, presenting a second VC and passing a value.
let destination3 = MyViewController()
let mediation3: (UIViewController, UIViewController) -> () = { source, destination in
if let destination = destination as? MyViewController {
destination.someData = "Sent message 3"
}
}
let navigation3 = SnapNavigation(
source: .viewController(self),
destination: .viewController(destination3),
mediation: .method(mediation3),
presentation: SnapNavigation.Presentation.present(true, {}))
navigator.navigate(using: navigation3)
}
}
如果您想跳过细节并立即使用SnapNavigation,以下示例配方提供了一些起点。
// Navigation using provider:
// Explicitly set the navigator data provider.
// We set ourself as the data provider in this example. See the SnapNavigatorDataSource extension below.
navigator.navigationProvider = self
navigator.navigate()
extension MyViewController: SnapNavigatorDataSource {
func navigation(for navigator: SnapNavigator) -> SnapNavigation {
let someDestination = UIViewController()
let someMediation: (UIViewController, UIViewController) -> () = { source, destination in
destination.title = "Honey I Set the Title!"
}
return SnapNavigation(
source: .viewController(self),
destination: .viewController(someDestination),
mediation: .method(someMediation),
presentation: .show)
}
}
基本可组合导航示例
// An example SnapNavigationSegue and SnapNavigationMediator in one.
// Intended as an example. Not to be used or subclassed.
//
// Use this approach to define a custom UIStoryboardSegue that uses an [already internally defined] Navigator and an internal mediation method when performing its transition.
// To implement your own class, copy this class and:
// - Use a custom unique Class name.
// - In a matching storyboard segue, set the class of the segue instance to this class.
// - Customize the mediation(source:destination:) method to dynamically craft the desired Navigation result.
// - Optional: The navigationIntent.presentation can be set, which will override any segue presentation method.
import UIKit
class ExampleNavigationSegue: SnapNavigationSegue {
override var mediation: (UIViewController, UIViewController) -> () {
get {
// Perform mediation here.
return { source, destination in
// A mediation might look like this:
// if let source = source as? ExpectedSourceSubclassOrProtocol,
// let destination = destination as? ExpectedDestinationSublassOrProtocol {
// destination.valueToSet = source.providingValue
// }
}
}
set {
// Irrelevant.
}
}
}
导航提供者示例
// An example SnapRouteNavigator.
// Intended as an example. Not to be used or subclassed.
//
// Use this approach to define a custom Navigator that holds internal Navigation data mapped to Route enum cases.
// To implement your own class, copy this class and:
// - Use a custom unique Class name.
// - Use a custom enum Route definition matching your navigation needs.
// - Customize the `navigation<Route>(for:)` method to dynamically craft the desired Navigation result.
//
// Example usage, from a UIViewController:
// myNavigator = ExampleRouteNavigator(source: self)
// myNavigator.navigate(using: ExampleRoute.presentSettings)
import UIKit
class ExampleRouteNavigator: SnapRouteNavigator {
var navigation: SnapNavigation
// MARK: - Initialization
init(source: UIViewController) {
navigation = SnapNavigation(source: source, destination: source)
}
// MARK: - Navigation
func navigation<Route: CaseIterable>(for route: Route) -> SnapNavigation? {
guard let route = route as? ExampleRoute else { return nil }
switch route {
case .presentSettings:
// Set destination / destinationFactory here.
// Set mediation here.
// Set presentation here.
return navigation
case .showColleagueView(let viewData):
// Set destination / destinationFactory here.
// Set mediation here.
// Set presentation here.
return navigation
case .showDetailView(let detailData):
// Set destination / destinationFactory here.
// Set mediation here.
// Set presentation here.
return navigation
}
}
}
enum ExampleRoute: CaseIterable {
// Conformance to CaseIterable.
static var allCases: [ExampleRoute] {
return [
.presentSettings,
.showColleagueView(viewData: 0),
.showDetailView(detailData: "")
]
}
case presentSettings
case showColleagueView(viewData: Int)
case showDetailView(detailData: String)
}
使用StoryboardSegue进行导航
路由导航程序示例
简介
- 在iOS UIKit框架的上下文中进行视图导航是一个涉及多个对象和动作的过程。导航从触发动作开始,确定起始源视图控制器和结果目标视图控制器。之后,以某种方式呈现目标视图控制器,最后在视图控制器上执行任何所需的转换。
- 聚合效用:为所有常见的导航触发提供默认的表示动画实现;在一个函数调用中处理整个导航流程。
- 分离紧密关联的关注点:最小化对同事视图的引用;将执行导航动作的需求与实际实现分离;视图实例化、调解和表示可以各自独立。
- 轻量级:作为一个库使用,而不是一个框架;通过组合而非继承来促进使用。
- 灵活:适用于由代码触发的导航或作为
UIStoryboardSegue
操作。
导航由模型类(SnapNavigation
和具体的路由对象)描述,其中导航器类管理导航动作(SnapNavigator
、SnapNavigationRouter
和 SnapNavigationSegue
)。具体的导航场景可以在您的应用中部分或完全定义为自定义导航模型,动态发送到导航对象的导航方法,或者根据需要混合采用每种方法的组合。
SnapNavigation:描述导航的数据模型
SnapNavigation类实例描述了一个特定的导航动作。
SnapNavigation有四个基本属性:source
、destination
、mediation
和 presentation
。每个都是枚举。共同描述了导航的谁、什么和如何。
source
是导航发出的代表视图对象。
destination
是导航过渡到的代表视图对象。
mediation
统御在 source
和 destination
上执行的任何转换方法。
presentation
统御 source
和 destination
之间的过渡动画和最终的表示。
导航必须描述一个 source
和 destination
,但其他属性是可选的。
只有 source
和 destination
的导航描述了一个抽象的关联。
有 mediation
但没有 presentation
的导航描述了一种纯粹的中介关系。例如,在 source
和 destination
之间进行数据打包。
有 presentation
但没有 mediation
的导航描述了一种纯粹的表现关系。例如,这可能是一个方法,在动画过程中将 destination
作为模态视图覆盖在 source
上。
同时有 mediation
和 presentation
的导航描述了一个完整的导航。mediation
和 presentation
一起可以定义在 NavigationIntent
中。
SnapNavigator:执行导航的对象
SnapNavigator 是一个执行导航的对象所采用的协议。
SnapNavigator
有一套强大的默认实现,使得实现此协议的对象不需要实现任何方法,除非需要定制行为。大多数导航定制可以通过操作 SnapNavigation
数据来执行。
提供了一系列的 navigate
方法,允许广泛的组合导航操作。这些方法分为两大类:使用 navigationProvider
进行导航,或者仅仅使用提供的参数进行导航。navigate(using:)
和 navigate(from:…)
方法仅使用提供的参数进行导航,而其他的所有 navigate
方法都是基于使用 navigationProvider
数据。
SnapNavigationSegue:实现自定义导航的 UIStoryboardSegue
SnapNavigationSegue 是一个实现了导航意图的基本 UIStoryboardSegue
类。
这是用于子类化的。如果一个子类没有自定义实现,它将表现为一个标准的 UIStoryboardSegue
。自定义 UIStoryboardSegue
类旨在处理自定义展示场景。使用 SnapNavigationSegue
可以实现完整的导航定制(调解和展示),将所有导航代码移动到 segue 类中,并且不需要在调用的 UIViewController
的 prepare(for:)
方法中处理导航。
要为你的子类提供自定义调解,只需重写 mediation
。这是主要用例。
或者,除了重写 mediation
,还可以重写 intent
、mediator
或 presentation
。设置 mediator
优先于 mediation
。设置 presentation
优先于在 perform
函数中触发的内部 UIStoryboardSegue
展示方法。
作为自定义专有子类的替代方案,可以使用依赖注入容器来设置所需的导航意图值。此类框架必须与 storyboαρd 实例化生命周期一起工作,在创建该对象但触发 perform
方法之前正确设置值。
SnapNavigationSegue
不会为嵌入式 segues 选项工作,也不会处理 unwind segue 方法。这两种 segues 类型都不是通过实例化 UIStoryboardSegue
来运行的。这符合预期,因为嵌入式和 unwind segues 行动的重点是视为视图控制器的包含责任。嵌入式 segue 描述了父视图控制器和子视图控制器之间的关系,其中子视图控制器的设置应在父 prepare(for:)
方法中处理。unwind segue 在 storyboαρd 已经建立的对象层次图中生效,其中 unwind 目标视图控制器应该通过自定义的 @IBAction
函数表达处理 unwind 的能力。如果需要,可以将嵌入式和 unwind 一起在一个扩展中组合起来以组织导航代码。
Routes:针对特定导航的强大便捷性
路由以 CaseIterable
类型表示。路由定义了具体的导航用例。它们允许从触发导航的对象中表达出大量的特定导航需求,解耦了导航实现的细节。一个 SnapNavigationRouter
将给定的路由映射到一个 SnapNavigation
,这个 SnapNavigation
可以供 SnapNavigator
使用。
定义为枚举并具有相关值的路由可以用来提供条件意图(例如,中介有效载荷),前提是它们遵循 CaseIterable
,并且与具体 SnapNavigationRouter
类的 navigation
函数切换配合使用。