Featured image of post 使用 Coordinator 模式处理 SwiftUI 与 UIKit 混合导航

使用 Coordinator 模式处理 SwiftUI 与 UIKit 混合导航

Coordinator 模式将传统场景下由 UIViewController 承担的导航职责抽离为单独的部分,来避免 ViewController 的过度膨胀,并促进 ViewController 的重用。

Coordinator 模式

2015 年,Soroush Khanlou 就向 Apple 社区介绍了 Coordinator 模式这一概念。简而言之,这一模式将传统场景下由 UIViewController 承担的导航职责抽离为单独的部分,来避免 ViewController 的过度膨胀,并促进 ViewController 的重用。

我们来看一下下面这段代码:

1
2
3
4
5
6
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  let selectedItem = data[indexPath.row]
  let detailViewController = DetailViewController(item: selectedItem)
  
  navigationController?.pushViewController(detailViewController, animated: true)
}

在上面的代码中,我们可以发现有两处耦合。

  1. TableViewDetailViewController
  2. TableViewUINavigationController

让我们逐项分析。第一,TableView 并不需要知道 DetailViewController 的具体类型。如果在这里确定了 DetailViewController,那么当 TableView 复用并需要推出其他的视图控制器时,处理起来会变的棘手。抽象出基类并针对不同场景创建子类并重写方法?这并不是一个好的选择。

第二,TableView 其实也并不需要知道是谁负责导航。试想一下这样的场景:如果 TableView 作为一个 UISplitViewController 的 Primary Column,那么它的职责是在用点击相应的行时,在 Secondary Column 中推出细节视图控制器,这里的代码同样需要修改。

如何解决以上的问题呢?我想可以抽离出导航部分的代码,创建为 Coordinator。这样,在不同场景下,我们只需要创建相应的 Coordinator 实例,就可以因地制宜的导航。

SwiftUI 与 UIKit 混合

随着 iOS 的不断发展,SwiftUI 逐渐在开发中被使用,Coordinator 在处理 SwiftUI 与 UIKit 混合时的导航,同样有着优势。

试想这样一个场景,我们需要在 A 视图控制器中,推出由 UIHostingController 包装的 SwiftUI B 视图,又需要在 B 视图中,推出 C 视图控制器。

看起来,可以有两种解决方案,在 B 视图中直接使用 NavigationLink 推出由 UIViewRepresentable 封装的 C 视图控制器,或者向 B 视图中传入 navigationController

经过实验,我们可以发现,可以直接在 B 视图中使用 NavigationLink 导航到 C 视图控制器,并且包裹 A 视图控制器的 UINavigationController 会自动作为 NavigationView / NavigationStackNavigationLink 配合生效。看起来 Apple 默默做的很好,但一旦事情变得复杂起来,例如需要 B 视图在 Secondary Column 中推出 C 视图控制器时,以上的两种方案均有问题。

方案一中,我们无法找到 showDetailViewController 在 SwiftUI 中的同等声明方式,因此无法实现相应的效果。而在方案二中,我们需要传入 splitViewController,这导致视图变得更加复杂。

Coordinator 模式应对混合

那么,不妨使用 Coordinator 模式来处理上述问题。在这里,我们用 router 表示 SwiftUI视图 / UIKit视图控制器持有的 Coordinator 实例。

我们在 SwiftUI 视图或者 UIKit 视图控制器创建时注入一个 router 实例,并在需要导航时,调用 router 的方法来处理导航。

这样,在不同的使用场景下,我们可以给 SwiftUI 视图或者 UIKit 视图控制器注入不同的 router,来实现不同的导航;视图控制器也不需要考虑具体的导航方式和下一个视图,只需提供数据即可;SwiftUI 视图也只需要使用 router 来处理一切即可。

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
Built with Hugo
主题 StackJimmy 设计