GoCoordinator 0.1.1

GoCoordinator 0.1.1

schelterstudios 维护。



  • 作者:
  • schelterstudios

GoCoordinator

Travis.com badge Version Swift 5.0 Platform License

(超级)早期的 GoCoordinator 构建。

安装

要将 GoCoordinator 添加到项目中,请将以下内容插入您的 Podfile
pod 'GoCoordinator', :git => 'https://github.com/schelterstudios/GoCoordinator.git'

使用 GoCoordinator 创建新应用

虽然 GoCoordinator 的真正优势在于其可扩展性,但您仍然可以通过几个步骤达到一个可工作的演示。尝试这份指南来熟悉设置过程。

开始时,让我们使用 iOS App 模板创建一个新的应用:选择 App 模板

接下来,将您的选项设置为 Storyboard 和 UIKit:新项目选项

现在项目文件已就位,请使用 安装说明 添加 GoCoordinator 框架。记住,重启项目时要使用 workspace 文件!

由于协调器与视图层次控制有关,我们不希望应用自动实例化视图控制器。为了防止这种情况发生,请选择您的 info.plist 并删除此处选中的两行:Storyboard 在 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 部署了您的第一个视图!协调器有许多内部功能,但最基本的设置流程是

  1. 实例化一个协调器。
  2. 获取它的 viewController 并将其插入视图层次结构中。
  3. 调用协调器的 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!"
}

接下来,创建一个新的视图用户界面,将其命名为与您的视图控制器相同的名称。 Creating the view

在 xib 中,选择 File's Owner 并将其类设置为 MyNibViewController。在输出面板中,将 view 绑定到您的 xib 视图。还可以添加一个 标签组件并将其绑定到 label。它应该看起来像这样: Setting up the Xib

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。我的按钮样式如下:我的 Xibs

现在运行你的应用。点击按钮现在应该会展示和关闭你的新视图。

协调器子类化

你为应用添加的视图控制器越多,你的协调器基础设施就会变得更加复杂。你也会想要一些高级功能,让协调器能够在视图控制器之间传递数据。你需要子类化协调器以定义其参数,并相应地初始化视图控制器,实际上,你可能每个视图控制器都有一个协调器子类。为了演示,我们将参考 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()
    }
}

其初始化方法有几个额外的参数,即 owneridentifier。在设置 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() 可以提高视图控制器抽象的层级。有了它,我们的控制器不必关心它是如何显示的。如果是被推动的,就弹出。如果是被呈现的,就消失。接下来,让我们看看 FriendMapViewControllerFriendMapCoordinatorFriendMapViewController 是一个独立的视图,以模态方式显示,因此我们将其设置为单独的 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 之类的模式。