容器视图控制器Redux

在上一篇文章中,我讨论了iOS中容器视图控制器与组合相关的用法。 在本文中,我想通过在一个具有协调器和多个内容屏幕的更复杂的应用程序中的插图来扩展该概念。 让我们开始吧!

以防万一您没有阅读上一篇文章,或者只是需要对视图控制器包含进行快速复习,我们将回顾基础知识。 自iOS 5以来,自定义容器视图控制器就已经存在,并且由Apple提供,部分是为了防止他们认为滥用应用程序中的视图层次结构。 当来自一个视图控制器的视图层次结构被“剥离”并放置在另一个控制器的视图层次结构中时,就会发生这种情况。 苹果公司提到这种方法会导致层次结构不一致。 不一致的层次结构会导致外观和旋转方法无法正确转发到层次结构的各个部分,进而导致意外行为和不良的用户体验。 苹果的演讲可以在这里找到: WWDC 2011 —第102节 。 在上一篇文章中可以找到添加子控制器的基本代码。

为了更好地演示容器视图控制器的用法,我创建了一个示例应用程序,该应用程序实现了各种包含方法。 这个应用程序旨在模拟一个真实世界的应用程序,而不仅仅是关注视图控制器的包含。 尽管视图控制器示例有些人为设计,但该应用程序使用视图控制器容器以及流协调器,协议,类扩展,依赖项注入和其他几种应用程序体系结构技术来尝试为视图控制器容器提供更广泛的上下文。 我们将遍历每个屏幕,并探索组成该屏幕的组件。

该应用程序的根视图控制器是一个UITabBarController由四个内容视图控制器组成。 第一个选项卡是自定义ContainerViewController的示例,该ContainerViewController是一个标准视图控制器,用于管理其他两个视图控制器的内容。 这旨在说明两个概念。 首先,一个图像加载视图控制器将图像简单地加载到视图控制器中(从Dave DeLong借来的一个概念)。 ImageViewControllerImageLoader对象初始化,该对象进行网络调用以检索图像。 就是这样,它无所不包,简短而简单。

其次,屏幕的下半部分是ListViewController ,它只是一个UIViewController ,其中包含一个UITableView ,该UITableView从Web加载一些JSON数据。 ListViewController使用ListLoader对象初始化,以进行网络调用以检索数据。 DataSource被分离成单独的ListDataSource,它只是一个Swift对象,用于管理节,行计数和单元格创建。 同样,非常简单明了。

这些控制器中的每一个都使用容器来添加LoadingViewController ,这是一个仅包含活动指示器的视图控制器。 我已经在多个应用程序中使用了此概念,它非常有用,尤其是当您具有自定义活动指示器时。 加载数据后,将删除加载视图控制器。 在ListViewController选择一行将过渡到新的ListDetailViewController 。 该控制器采用像以前的视图控制器一样的ImageLoader对象来加载较大版本的图像。 我们也可以在这里使用图像加载视图控制器,但是我想展示如何覆盖loadView以在视图控制器的代码中提供自定义视图。 此方法在以后的视图控制器中再次使用。

ListViewController还实现了窥视和弹出动作,这些动作正逐渐成为应用程序中预期的用户体验。 实施此行为时,会发现协调员存在问题。 协调器用于弹出动作,就像用于任何其他导航流一样。 但是,peek直接在ListViewController 。 这与期望协调器模式如何工作相反,因为视图控制器不应该知道任何其他视图控制器。 问题就变成了谁应该处理该视图控制器的销售? 列表视图控制器对协调器的存在一无所知。 我们是否应该将其移到ListViewController协调器中并注入该协调器,以便列表可以请求窥视视图控制器? 我们是否应该考虑将工厂对象出售给窥视实例的视图控制器? 我仍然需要回答这个问题,因为理想情况下,我们希望ListViewController能够基于列表内容显示不同的详细信息屏幕。 在当前的实现中这是不可能的。

该概念由Halide应用程序的Ben Sandofsky和Dave DeLong提出,可以替代使用协调器进行应用程序流程。 这个想法是使用容器视图控制器作为封装应用程序流的一种方式。 作为示例,考虑图像拾取流程。 这样的流程可能包含多个步骤,例如呈现选取器/相机,捕获图像,应用滤镜,然后保存或发布图像。 这可能涉及三个视图控制器,并且所有三个将包含在单个容器视图控制器中。 应用程序只需要提供一个单一的视图控制器,即可管理在容器视图控制器中处理的流。 本质上,容器控制器充当图像拾取流程的协调器。 这消除了协调器可能附带的样板代码和通信。

这个示例应用程序中发生了很多事情,这就是重点。 我想展示如何在实际的应用程序中使用视图控制器容器,而不是集中在单个屏幕上。 涵盖的许多概念是从其他作者和发言人那里学到的。 视图控制器包含,小型联网(来自objc.io),流程协调器(请参阅我的上一篇文章)。 将问题分解为可管理的小部件,然后将其重新组装为复杂的解决方案的能力是编程中最重要的方面之一。 视图控制器不需要管理整个屏幕内容,如果需求很复杂,它只能管理屏幕的一部分甚至单个元素。 通过尝试新方法和新想法,我们可以验证(或无效)我们的假设。 我们在构建应用程序时尝试的事情越多,我们就会学到更多。

最终项目可以在这里找到:ViewControllerContainment

参考作者
如果您对本文中提到的任何作者感兴趣,可以在Twitter上关注他们,网址为@ davedelong, @ johnsundell, @ twostraws(Paul Hudson)和@khanlou(Soroush Khanlou)

资料来源
如何将视图代码移出视图控制器
在Swift中使用子视图控制器作为插件
Swift中的自定义容器视图控制器
如何使用视图控制器包含
UIKonf18 — MVC不是您的问题
瑞士移动开发者协会-更好的MCV
AdaptiveElements:使用UIKit实现自己的自适应设计
本·桑多夫斯基
布莱恩·安文特(Brian Advent)
objc.io
MVCTodo
免费的JSON网站
网络链接调节器(模拟慢速网络)
苏格拉底Wikipedia Commons
冥想图像
大力神港图像
泰姬玛哈陵(Rajhu Nayyar)在Unsplash上
Dmitriy Ilkevich在Unsplash上​​推出的女士/靴子