繁体   English   中英

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

[英]iOS Swift Coordinator pattern and back button of Navigation Controller

我正在使用模式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)
    }
}

在阅读了许多关于协调器的文章并看到了一些复杂的想法(如路由器、一些令人困惑的魔法和自定义导航控制器委托)之后,我现在所做的是:

View Controller 强烈拥有 Coordinator,而 Coordinator 对 View Controller 的引用也很弱(如果有的话)。 Coordinator 对他的父对象有弱引用,以支持 Coordinator 对象之间通信的责任链。

(责任链设计模式的例子是 iOS 中的响应者链。)

当你在某个协调器上调用 stop 的那一刻,视图控制器从堆栈中弹出,释放和释放协调器。 因此,当点击后退按钮并关闭视图控制器时,协调器将被释放。

这对我有用,因为不需要构建额外的基础设施。

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

但是,最后,当看到有多少代码和不必要的复杂性时,我选择了拥有 Coordinator 的 View Controller。 我只希望对象位于视图控制器之上,以保持数据提供者、服务、视图模型等,以便视图控制器更干净。 我不想重新发明协调器的推送弹出堆栈并处理这么多所有者问题。 就像我想要一些东西来缓解我的生活而不是让它变得更加复杂..

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

协调器模式在原生后退按钮方面有一个已知的盲点。 您主要有两种方法来修复它:

  • 重新实现你自己的后退按钮,虽然你失去了原生的向后滑动手势来导航回来。
  • 实现UINavigationControllerDelegate以检测何时弹出视图以便能够解除分配匹配的协调器。

关于第一个解决方案,我不推荐这个,用户会为你的代码架构付出代价,这听起来不公平。

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

在这里写了一些关于它的内容,详细介绍了主要步骤。

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

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

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

    navigationController.pushViewController(inStoreMainController, animated: true)
}

可以在此处找到使用此样式的示例应用程序: https : //github.com/danielt1263/RxMyCoordinator

除了此处发布的其他答案之外,我建议您可以将startNameOfYourScreen(...)方法放在主AppCoordinator类中的每个视图控制器或您命名的任何内容中,并且根本不要打扰子协调器,尤其是如果您不这样做在你的应用中有大量的屏幕。

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM