GoCoordinator
(超级)早期的 GoCoordinator 构建。
安装
要将 GoCoordinator 添加到项目中,请将以下内容插入您的 Podfile
pod 'GoCoordinator', :git => 'https://github.com/schelterstudios/GoCoordinator.git'
使用 GoCoordinator 创建新应用
虽然 GoCoordinator 的真正优势在于其可扩展性,但您仍然可以通过几个步骤达到一个可工作的演示。尝试这份指南来熟悉设置过程。
接下来,将您的选项设置为 Storyboard 和 UIKit:
现在项目文件已就位,请使用 安装说明 添加 GoCoordinator 框架。记住,重启项目时要使用 workspace 文件!
由于协调器与视图层次控制有关,我们不希望应用自动实例化视图控制器。为了防止这种情况发生,请选择您的 info.plist 并删除此处选中的两行:
如果在此时运行您的应用,模拟器应该只显示一个黑色屏幕。这很好!我们将编写一个协调器来添加到窗口的根窗口。打开您的 SceneDelegate 并将以下代码添加到您的导入中
import GoCoordinator
为您的协调器添加一个属性
private(set) var coordinator: ManualCoordinator?
然后使用以下代码替换第一个方法
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
let vc = UIViewController()
vc.view.backgroundColor = .magenta
coordinator = ManualCoordinator(viewController: vc)
window?.rootViewController = coordinator?.viewController
coordinator?.start()
window?.makeKeyAndVisible()
}
现在构建您的应用,您应该会看到一个漂亮的洋红色屏幕。恭喜您,您刚刚用 GoCoordinator 部署了您的第一个视图!协调器有许多内部功能,但最基本的设置流程是
- 实例化一个协调器。
- 获取它的
viewController
并将其插入视图层次结构中。 - 调用协调器的
start()
方法。
切换到 NibCoordinator
ManualCoordinators非常适合用于测试,但您可能不会在测试之外依赖它们。NibCoordinators 和 StoryboardCoordinators 要有用得多,因为它们利用了 xibs 和 storyboards 来与 UIKit 交互。让我们设置一个使用 xib 的视图控制器。首先,创建一个新的 UIViewController 并将其命名为 MyNibViewController。让我们添加以下代码
@IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
label.text = "Hello, World!"
}
接下来,创建一个新的视图用户界面,将其命名为与您的视图控制器相同的名称。
在 xib 中,选择 File's Owner 并将其类设置为 MyNibViewController。在输出面板中,将 view 绑定到您的 xib 视图。还可以添加一个 标签组件并将其绑定到 label。它应该看起来像这样:
在 SceneDelegate 中,将您的协调器属性更改为
private(set) var coordinator: NibCoordinator<MyNibViewController>?
然后使用以下代码替换第一个方法
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
coordinator = NibCoordinator<MyNibViewController>()
window?.rootViewController = coordinator?.viewController
coordinator?.start()
window?.makeKeyAndVisible()
}
运行它以查看您的新视图。按照惯例,xibs 通常会与它们的 file's owner 名称相同。我们在 GoCoordinator 中利用这一点来实例化视图控制器并将其 nib 自动附加。您需要做的只是使用 泛型 声明适用于 NibCoordinator 的视图控制器。
使用 Go 进行导航
与协调器一起工作的好处很多,但没有导航,我们的前进速度也不会很快。GoCoordinator 为视图控制器提供了一个名为 go 的强大钩子。使用 go 通过各自的协调器推动、弹出、显示和消失视图。让我们在应用中进行尝试。首先,添加 MyNib2ViewController swift 和 xib 文件。在类文件中,添加以下代码
@IBAction func back(_ sender: Any?) {
go.dismiss()
}
由于 GoCoordinator 在您项目中,因此您的视图控制器自动具有对 go 的访问权限。这是 MyNib2ViewController 隐藏其协调器(本身的过程)的唯一逻辑。现在转到您的 MyNibViewController 类。您可以使用自己喜欢的逻辑,并添加 import GoCoordinator
。添加以下代码
@IBAction func next(_ sender: Any?) {
let coordinator = NibCoordinator<MyNib2ViewController>().asAnyCoordinator()
go.present(coordinator:coordinator)
}
这里有一些注意事项。首先,视图控制器不应该与协调器进行交互,除了实例化和传递给 go。我们甚至不需要在这里调用 start()
,因为协调器内部已经处理了这一点。其次,是 asAnyCoordinator()
的介绍。这个方法通过将其包装进 AnyCoordinator 来消除我们的协调器的具体实现以及泛型。如果你没有理由保持具体引用,你可能会更喜欢 AnyCoordinator,因为它让你能够以更抽象的方式存储和交互。每次你将协调器传递给 go 时,你都必须首先消除具体实现。现在,给你的 nib 添加按钮并将它们绑定到你的 IBAction。我的按钮样式如下:
现在运行你的应用。点击按钮现在应该会展示和关闭你的新视图。
协调器子类化
你为应用添加的视图控制器越多,你的协调器基础设施就会变得更加复杂。你也会想要一些高级功能,让协调器能够在视图控制器之间传递数据。你需要子类化协调器以定义其参数,并相应地初始化视图控制器,实际上,你可能每个视图控制器都有一个协调器子类。为了演示,我们将参考 GoCoordinatorExamples 项目。首先,让我们看一下 FriendsCoordinator。
import UIKit
import GoCoordinator
class FriendsCoordinator: StoryboardCoordinator<FriendsViewController> {
private let friends: [Contact]
override init() {
friends = (0..<10).map{ _ in random_contact() }
super.init()
}
override func start() throws {
viewController.friends = friends
try super.start()
}
}
由于 FriendsViewController 是一个 Storyboard 视图控制器,我们继承自 StoryboardCoordinator 并声明它的视图控制器为类型 FriendsViewController。我们重写其初始化方法,以便能够捕获要传递给其视图控制器的数据,并重写 start()
方法来传递数据。就是这样!现在,一旦你在协调器层次结构中添加 FriendsCoordinator,它将自动调用 start()
并为你加载 FriendsViewController。
特别说明:Storyboard 协调器的默认行为是查找与其前缀匹配的 Storyboard。(例如:FriendsCoordinator 会查找 Friends.storyboard。)如果没有分配 StoryboardOwner,它还会查找带有 Is Initial View Controller 标志的视图控制器。
现在,让我们转到第二个协调器 FriendInfoCoordinator。这个像上一个一样也是一个 Storyboard 协调器,但有一些差异。让我们看看。
class FriendInfoCoordinator: StoryboardCoordinator<FriendInfoViewController> {
private let friend: Contact
private weak var delegate: FriendInfoViewControllerDelegate?
init(friend: Contact, delegate: FriendInfoViewControllerDelegate?, owner: StoryboardOwner) {
self.friend = friend
self.delegate = delegate
super.init(owner: owner, identifier: "contact-details")
}
override func start() throws {
viewController.friend = friend
viewController.delegate = delegate
try super.start()
}
}
其初始化方法有几个额外的参数,即 owner 和 identifier。在设置 Storyboard 时,一个视图控制器被指定为初始视图控制器。我们将该视为 Storyboard 的顶层所有者。任何额外的视图控制器基本上都是那个空间的租户,不能独立实例化。实例化后,租户可以被视为 Storyboard 中其他租户的所有者。标识符只是你在 Storyboard 中分配给视图控制器的 Storyboard ID。之后的设置和启动都是标准的。现在,让我们将它们连接起来。在 FriendsCoordinator 中,我们已经添加了此方法
func pushInfo(friend: Contact, delegate: FriendInfoViewControllerDelegate?) {
let coordinator = FriendInfoCoordinator(friend: friend, delegate: delegate, owner: self)
try! push(coordinator: coordinator.asAnyCoordinator())
}
通常情况下,如果一个协调器始终被另一个协调器推动,我会明确将该逻辑定义为父协调器的一个自定义方法。由于 FriendInfoCoordinator 需要来自同一故事的拥有者,因此预期我们只能从我们的顶级协调器推动它。为了从视图控制器内部访问您的自定义协调器方法,请使用 go(as:)
并传入协调器类型。 FriendsViewController 如此调用 pushInfo
go(as: FriendsCoordinator.self).pushInfo(friend: friends[indexPath.row], delegate: self)
然而,FriendInfoCoordinator 没有自定义方法,所以我们不需要为它使用 go(as:)
。 FriendInfoViewController只需调用 go.pop()
或 go.popOrDismiss()
即可返回上一个视图控制器。
特别说明:使用
go.popOrDismiss()
可以提高视图控制器抽象的层级。有了它,我们的控制器不必关心它是如何显示的。如果是被推动的,就弹出。如果是被呈现的,就消失。接下来,让我们看看 FriendMapViewController 和 FriendMapCoordinator。 FriendMapViewController 是一个独立的视图,以模态方式显示,因此我们将其设置为单独的 nib。如您之前所看到的,我们可以在应用程序中混合匹配不同的协调器类型。就像与 StoryboardCoordinator 一样,您可以继承 NibCoordinator。以下是我们的示例
import UIKit
import GoCoordinator
class FriendMapCoordinator: NibCoordinator<FriendMapViewController> {
private let friend: Contact
init(friend: Contact) {
self.friend = friend
}
override func start() throws {
viewController.friend = friend
try super.start()
}
}
看起来很熟悉,是吧?现在,就像 FriendInfoCoordinator 一样,我们需要一种优雅的方式来呈现它。我们可以在其他协调器中添加一个自定义方法,或者我们可以创建一个协调器扩展。协调器扩展通常更适用于独立视图,因为它们将成为全局可访问的。我们可以直接在 FriendMapCoordinator 声明所在的同一类文件中添加扩展,如下所示
extension Coordinator {
func presentFriendMap(friend: Contact) {
let root = FriendMapCoordinator(friend: friend)
let coordinator = UINavigationCoordinator(root: root.asAnyCoordinator())
present(coordinator: coordinator.asAnyCoordinator())
}
}
记住,“go”钩子访问通用协调器方法,所以通过将 presentFriendMap 添加到 Coordinator,我们可以像在 FriendInfoViewController 中一样通用地调用它
go.presentFriendMap(friend: friend)
这就全部完成了!通过协调器子类化,您可以简化视图的实例化,并为视图与协调器之间的更多通信提供手段。它还允许您采用诸如 Delegate(如您在示例中所看到的那样)和 MVVM 之类的模式。