避免使用遏制和子视图控制器的大规模视图控制器

View Controller是提供基本构建块的组件,我们以此为基础来构建iOS开发中的应用程序。 在Apple MVC世界中,它充当View和Model之间的中间人,充当两者之间的协调器。 它以控制器作为观察者开始,该观察者对Model的更改做出反应,更新View,使用Target Action接受来自View的用户交互,然后更新Model。

作为iOS开发人员,即使我们使用MVVM,MVP或VIPER之类的体系结构,在很多情况下我们也将面临处理Massive View Controller的问题。 有时,View Controller的职责太多,无法在一个屏幕中处理。 它违反了SRP(单一责任原则),在模块之间建立了紧密的耦合,并使得重用和测试每个组件变得困难。

我们可以以下面的应用屏幕截图为例。 您可以在一个屏幕上看到至少3个职责:

  1. 显示电影列表;
  2. 显示可以选择应用于电影列表的过滤器列表;
  3. 清除所选过滤器的选择。

如果我们要使用Single View Controller构建此屏幕,则可以确保View Controller会变得非常庞大和肿胀,因为它在一个单一View Controller中处理过多的职责。

我们如何解决这个问题? 解决方案之一是使用View Controller Containment和Child View Controller。 使用此解决方案的好处如下:

  1. 将电影列表封装到MovieListViewController ,后者仅负责显示电影列表并对电影模型中的更改做出反应。 如果我们只想显示没有过滤器的电影列表,那么我们也可以在另一个屏幕中重用此MovieListViewController
  2. 将过滤器逻辑的列表和选择封装到FilterListViewController ,后者仅负责显示和处理过滤器的选择。 当用户选择和取消选择过滤器时,我们可以使用委托与父级View Controller通信。
  3. 将主View Controller缩小为一个ContainerViewController ,该ContainerViewController只是负责将“过滤器列表”中的选定过滤器应用于MovieListViewControllerMovie模型。 它还设置布局,并使用容器视图添加子视图控制器。

您可以在下面的GitHub Repository中查看完整的项目源代码。

alfianlosari / Filter-MVC-iOS

使用子级View Controller进行封装,可重用,并避免使用Massive View Controller…

github.com

使用Storyboard组成View Controller

根据上述情节提要,以下是我们用于构建“筛选器”屏幕的视图控制器:

  1. ContainerViewController :ContainmentView Controller提供2个容器视图,以将Child View Controller嵌入水平UIStackView 。 它提供了一个UIButton来清除选定的过滤器。 它还嵌入在充当初始View Controller的UINavigationController中。
  2. FilterListMovieController :View Controller是UITableViewController的子类,具有分组样式和一个原型标准Cell,用于显示过滤器的名称。 它还为其分配了Storyboard ID,因此可以以编程方式从ContainerViewController实例化它。
  3. MovieListViewController :视图控制器,它是UITableViewController的子类,具有纯风格和一个原型字幕Cell来显示Movie的属性。 像FilterListViewController一样,还为其分配了Storyboard ID。

电影列表视图控制器

此视图控制器负责显示作为实例属性公开的Movie模型的列表。 我们正在使用Swift didSet属性观察器对模型中的更改做出反应,然后重新加载UITableView 。 单元格使用默认字幕UITableViewCellStyle显示Movie的标题,持续时间,等级和体裁。

筛选器列表视图控制器

过滤器列表在3个单独的部分中显示MovieFilter枚举: 类型等级持续时间MovieFilter枚举本身符合Hashable协议,因此可以使用每个枚举的哈希值及其属性Set唯一存储在Set 。 筛选器的选择存储在Set包含MovieFilter的实例属性下。

为了与其他对象通信,使用了FilterListControllerDelegatedelegate模式。 委托有3种实现方法:

  1. 选择过滤器。
  2. 取消选择过滤器。
  3. 清除所有选定的过滤器。

集成在Container View Controller中

ContainerViewController ,我们有几个实例属性:

  1. FilterListContainerViewMovieListContainerView :将用于添加子视图控制器的容器视图。
  2. FilterListViewControllerMovieListViewController :对将使用情节提要ID实例化的电影列表和滤镜列表视图控制器的引用。
  3. movie :使用默认的硬编码电影实例化的Movie数组。

viewDidLoad被调用时,我们调用该方法来设置子视图控制器。 它执行以下几个任务:

  1. 使用情节提要ID实例化FilterListViewControllerMovieListViewController
  2. 将它们分配给实例属性;
  3. MovieListViewController分配给MovieListViewController
  4. ContainerViewController分配为FilterListViewControllerdelegate ,以便它可以响应过滤器的选择;
  5. 设置子视图框架,并使用辅助方法扩展将它们添加为子视图控制器。

对于FilterListViewControllerDelegate实现,选择或取消选择过滤器时,将针对每种流派,等级和持续时间过滤默认的Movies数据。 然后,将过滤器的结果分配给MovieListViewControllermovies属性。 对于所有过滤器的取消选择,它仅分配默认的电影数据。

通过查看示例项目,我们可以看到在我们的应用程序中使用View Controller Containment和Child View Controller的好处。 我们可以将单个View Controller的职责划分为仅具有单个职责(SRP)的单独View Controller。 我们还需要确保子视图控制器对父视图控制器一无所知。 为了使子视图控制器能够与父视图通信,我们可以使用委托模式。

这种方法还提供了松散耦合模块的优势,可以带来更好的可重用性和每个组件的测试。 随着应用程序变得越来越大,越来越复杂,它确实有助于我们扩展应用程序。 让我们继续向所有人学习📖,圣诞快乐🎄和新年快乐🎊! 跟着可可继续前进!!😋


在社交媒体平台上关注我们:
面子书:facebook.com/AppCodamobile/
Twitter:twitter.com/AppCodaMobile
Instagram的:instagram.com/AppCodadotcom

如果您喜欢这篇文章,请单击👏按钮并分享以帮助其他人找到它! 随时在下面发表评论。