【问题标题】:Reusing View Controller within Coordinator pattern在协调器模式中重用视图控制器
【发布时间】:2019-02-09 15:52:02
【问题描述】:

我的 iOS 应用中有一个 ViewController 流 (Flow A)。流程 A 相当复杂(根据情况,一些视图控制器会提前显示,或者根本不显示,等等)。为了处理这个问题,我使用了协调器模式。

代码(简化):

protocol Coordinator {
    func start()
}

protocol FlowACoordinatable {
    var coordinator: FlowACoordinator
}

class FlowACoordinator: Coordinator {

    private var navigationController: UINavigationController

    private var firstVC: FirstViewController
    private var secondVC: SecondViewController

    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }

    func start() { ... }

    func present(_ viewController: (FlowACoordinatable & UIViewController)) {
        viewController.coordinator = self
        self.navigationController.pushViewController(viewController, animated: true)
    }

    ...
}

class FirstViewController: UIViewController, FlowACoordinatable {

    var coordinator: FlowACoordinator?

    func buttonTapped() {
        self.coordinator?.goToNextStep()
    }
}

....

FlowACoordinator 包含有关如何以及何时使用present() 方法呈现视图控制器的逻辑。到目前为止一切顺利。

现在我有第二个流程Flow B,与流程A 大不相同。除了我想在两者之间共享一个视图控制器,我们称之为SharedViewController。这就是事情变得奇怪的地方,因为我不知道如何在两者之间共享这个视图控制器。

问题:我有两种方式的通信 - 协调器将自己设置为它呈现的视图控制器的协调器,并且视图控制器调用协调器上的方法作为对用户交互的响应。 SharedViewController 由两个 Coordinator 之一管理,并且它必须以某种方式将信息传递给当前的 Coordinator,不管它是哪一个。

到目前为止,我找到了两个解决方案,它们都不令人满意:

  1. 一个额外的协调器只处理SharedViewController - 这是很多开销并且很大程度上违背了协调器的目的。

  2. SharedViewController 中实现FlowACoordinatableFlowBCoordinatable、...,并具有多个协调器属性并在适当的时候调用它们。还有很多开销、样板代码和对协调器的调用。

关于如何很好地解决这个问题的任何想法?

【问题讨论】:

    标签: ios swift design-patterns coordinator-pattern


    【解决方案1】:

    我有同样的情况,我也不确定处理它的最佳方案是什么。我有一个必须在不同的协调器中使用的 viewController。

    当给定的 viewController 本身不需要协调器时是可以的。例如,我们称它为 DisplayPopupViewController。 我创建了一个名为 CanDisplayPopupProtocol 的协议:

    protocol CanDisplayPopupProtocol {}
    
    extension CanDisplayPopupProtocol where Self: Coordinator {
        func toDisplayPopupViewController() {
            let vc = DisplayPopupViewController.instantiate()
            navigationController.pushViewController(vc, animated: true)
        }
    }
    

    然后在 Coordidnator1 中:

    extension Coordinator1: CanDisplayPopupProtocol{}
    

    在 Coordinator2 中:

    extension Coordinator2: CanDisplayPopupProtocol{}
    

    现在两个协调器都有 toDisplayPopupViewController() 方法。

    正如我之前所说,当我不需要将协调器传递给 viewController 时,这很好,在这种情况下,DisplayPopupViewController 不需要协调器,因为它将被解除并且不需要任何导航。

    但是在这种情况下,当需要为 viewController 分配一个协调器时,它会变得更加复杂,我通过哪个协调器?

    我发现在我看来不是很优雅的解决方案是将 viewController 中的协调器类型更改为 Coordinator 协议,所以不要这样:

    weak var coordinator: Coordinator1?
    

    我会用:

    weak var coordinator: Coordinator?
    

    然后在 CanDisplayPopupProtocol 中,我将测试我正在处理的协调器并将正确的协调器分配给 viewController,如下所示:

    protocol CanDisplayPopupProtocol {}
    
    extension CanDisplayPopupProtocol where Self: Coordinator {
        func toDisplayPopupViewController() {
            let vc = DisplayPopupViewController().instantiate()
            switch self {
            case is Coordinator1:
                vc.coordinator = self as? Coordinator1
            case is Coordinator2:
                vc.coordinator = self as? Coordinator2
            default: break
            }
            navigationController.pushViewController(vc, animated: true)
        }
    }
    

    这并不漂亮,而且还有另一个缺点。在 DisplayPopupViewController 中,每次我需要使用协调器的方法之一时,我都需要测试我使用的是哪种协调器类型。

    switch coordinator {
    case is Coordinator1:
        (coordinator as! Coordinator1).toDisplayPopupViewController()
    case is Coordinator2:
        (coordinator as! Coordinator2).toDisplayPopupViewController()
    default: break
    }
    

    我确信这不是协议的最佳用途,希望关注此线程的人能对这个问题有更好的解决方案。

    【讨论】:

    • 我还没有在自己的项目中实现这个,但我目前计划在此基础上改进我们的实现:github.com/radianttap/Coordinator。他们将协调器注册为 UIResponder 链的一部分,从而可以通过响应器链传递消息,而 UIViewController 不知道协调器的任何细节(甚至没有明确的引用)。看起来很有希望,一旦我自己实现它,我会写一个答案。
    猜你喜欢
    • 2013-01-19
    • 1970-01-01
    • 1970-01-01
    • 2019-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-11
    • 1970-01-01
    相关资源
    最近更新 更多