iOS Swift Coordinator模式和导航控制器的后退按钮

时间:2019-01-12 02:50:07

标签: ios swift mvvm rx-swift coordinator-pattern

我正在使用模式MVVM+Coordinator。我的每个控制器都是由coordinators创建的。但是,点击Navigation控制器的后退按钮时,停止协调器的正确方法是什么?

class InStoreMainCoordinator: NavigationCoordinatorType, HasDisposeBag {

    let container: Container

    enum InStoreMainChildCoordinator: String {
        case menu = "Menu"
        case locations = "Locations"
    }

    var navigationController: UINavigationController
    var childCoordinators = [String: CoordinatorType]()

    init(navigationController: UINavigationController, container: Container) {
        self.navigationController = navigationController
        self.container = container
    }

    func start() {
        let inStoreMainViewModel = InStoreMainViewModel()
        let inStoreMainController = InStoreMainController()
        inStoreMainController.viewModel = inStoreMainViewModel

        navigationController.pushViewController(inStoreMainController, animated: true)
    }
}

5 个答案:

答案 0 :(得分:1)

我的方法是使用管理子协调器的根(父)协调器,因此,当用户完成流程或点击后退按钮时,将调用根协调器中的委托方法,它可以清理子协调器并创建新的如果需要的话一个。

答案 1 :(得分:1)

在阅读了许多有关协调器的文章并看到了一些复杂的想法(如路由器),一些令人费解的魔术和自定义的导航控制器代表后,我现在要做的是:

View Controller强烈拥有Coordinator,并且Coordinator对View Controller的引用很少。 协调者对父母的参考很少,以支持“责任链”协调对象之间的通信。

(“责任链”设计模式的示例为iOS中的“响应者链”。)

当您在某个协调器上停止调用时,视图控制器会从堆栈中弹出,释放并释放协调器。因此,当点击“后退”按钮并关闭视图控制器时,协调器将被释放。

这对我有用,因为无需构建其他基础结构。

最初,我通过构建符合UINavigationControllerDelegate协议的NavigationControllerMutliDelegate类解决了UINavigationControllerDelegate问题。它具有注册/注销逻辑。然后,此对象被传递给每个协调器,以在视图控制器关闭时通知协调器。 NavigationControllerMutliDelegate是Visitor设计模式的一个示例,它具有一堆协调器,并且在View Controller出现/关闭时,它通过向每个对象发送对象来通知所有协调器。

但是,最后,当看到有多少代码和不必要的复杂性时,我只是选择了拥有协调器的View Controller。我只希望对象位于View Controller上方,以保留数据提供程序,服务,视图模型等内容,以使View Controller更加整洁。我不想重塑协调器的推栈,而要处理如此多的所有者问题。就像我想要减轻生活压力的事情,而不是让事情变得更加复杂。

答案 2 :(得分:0)

协调器模式具有有关本机后退按钮的已知盲点。您主要有两种解决方法:

  • 重新实现您自己的后退按钮,尽管您松开了本机的向后滑动手势以向后导航。
  • 执行UINavigationControllerDelegate,以检测何时弹出视图以能够取消分配匹配的协调器。

关于第一个解决方案,我不建议这样做,用户应该为您的代码体系结构付出代价,这听起来不公平。

对于第二个,您可以按照@mosbah的建议将其实现到协调器本身,但是我建议您进一步使用NavigationControllerRouter将导航导航到协调器类以隔离导航本身并保持清晰的关注点分离。

here对此进行了详细介绍,详细介绍了主要步骤。

答案 3 :(得分:0)

我的解决方案是使用函数而不是类作为协调器。这样我就完全没有所有权问题。当按下后退按钮时,来自视图控制器的视图将发出已完成的事件,一切自然而然地展开,无需我费力。

您在示例中显示的start()可以通过以下方式更简单地表示:

func startInStore(navigationController: UINavigationController) {
    let inStoreMainViewModel = InStoreMainViewModel()
    let inStoreMainController = InStoreMainController()
    inStoreMainController.viewModel = inStoreMainViewModel

    navigationController.pushViewController(inStoreMainController, animated: true)
}

可以在以下位置找到使用这种样式的示例应用程序:https://github.com/danielt1263/RxMyCoordinator

答案 4 :(得分:0)

除了这里发布的其他答案之外,我建议您可以将每个视图控制器的 startNameOfYourScreen(...) 方法放在您的主 AppCoordinator 类或其他任何类中您为它命名并且完全不打扰儿童协调员,特别是如果您的应用中没有大量屏幕。

如果您使用闭包而不是委托模式,您可以在单个函数中编写与一个屏幕相关的所有内容(包括将其推送到屏幕上以及处理与从该屏幕移动到不同屏幕相关的事件)。这样你就不会像使用额外的委托方法那样弄得一团糟,因为你的主协调器中每个屏幕只有一个方法。

您还可以将这些功能拆分为 AppCoordinator 类的扩展并将它们放入单独的文件中,以便在您的项目中更好地组织,仅此而已。这样您就不会遇到后退按钮的任何问题,因为您根本不实例化子协调器,也不需要释放它们。

但是,如果您决定要使用子协调器的方式,那么这里有几个关于使用子协调器时后退按钮问题的可能解决方案的文章链接: