避免使用遏制和子视图控制器的大规模视图控制器
View Controller是提供基本构建块的组件,我们以此为基础来构建iOS开发中的应用程序。 在Apple MVC世界中,它充当View和Model之间的中间人,充当两者之间的协调器。 它以控制器作为观察者开始,该观察者对Model的更改做出反应,更新View,使用Target Action接受来自View的用户交互,然后更新Model。
作为iOS开发人员,即使我们使用MVVM,MVP或VIPER之类的体系结构,在很多情况下我们也将面临处理Massive View Controller的问题。 有时,View Controller的职责太多,无法在一个屏幕中处理。 它违反了SRP(单一责任原则),在模块之间建立了紧密的耦合,并使得重用和测试每个组件变得困难。
我们可以以下面的应用屏幕截图为例。 您可以在一个屏幕上看到至少3个职责:
- 显示电影列表;
- 显示可以选择应用于电影列表的过滤器列表;
- 清除所选过滤器的选择。
如果我们要使用Single View Controller构建此屏幕,则可以确保View Controller会变得非常庞大和肿胀,因为它在一个单一View Controller中处理过多的职责。
我们如何解决这个问题? 解决方案之一是使用View Controller Containment和Child View Controller。 使用此解决方案的好处如下:
- 将电影列表封装到
MovieListViewController
,后者仅负责显示电影列表并对电影模型中的更改做出反应。 如果我们只想显示没有过滤器的电影列表,那么我们也可以在另一个屏幕中重用此MovieListViewController
。 - 将过滤器逻辑的列表和选择封装到
FilterListViewController
,后者仅负责显示和处理过滤器的选择。 当用户选择和取消选择过滤器时,我们可以使用委托与父级View Controller通信。 - 将主View Controller缩小为一个
ContainerViewController
,该ContainerViewController
只是负责将“过滤器列表”中的选定过滤器应用于MovieListViewController
的Movie
模型。 它还设置布局,并使用容器视图添加子视图控制器。
您可以在下面的GitHub Repository中查看完整的项目源代码。
alfianlosari / Filter-MVC-iOS
使用子级View Controller进行封装,可重用,并避免使用Massive View Controller…
github.com
使用Storyboard组成View Controller
根据上述情节提要,以下是我们用于构建“筛选器”屏幕的视图控制器:
-
ContainerViewController
:ContainmentView Controller提供2个容器视图,以将Child View Controller嵌入水平UIStackView
。 它提供了一个UIButton
来清除选定的过滤器。 它还嵌入在充当初始View Controller的UINavigationController
中。 -
FilterListMovieController
:View Controller是UITableViewController
的子类,具有分组样式和一个原型标准Cell,用于显示过滤器的名称。 它还为其分配了Storyboard ID,因此可以以编程方式从ContainerViewController
实例化它。 -
MovieListViewController
:视图控制器,它是UITableViewController
的子类,具有纯风格和一个原型字幕Cell来显示Movie
的属性。 像FilterListViewController
一样,还为其分配了Storyboard ID。
电影列表视图控制器
此视图控制器负责显示作为实例属性公开的Movie
模型的列表。 我们正在使用Swift didSet
属性观察器对模型中的更改做出反应,然后重新加载UITableView
。 单元格使用默认字幕UITableViewCellStyle
显示Movie
的标题,持续时间,等级和体裁。
筛选器列表视图控制器
过滤器列表在3个单独的部分中显示MovieFilter
枚举: 类型 , 等级和持续时间 。 MovieFilter
枚举本身符合Hashable
协议,因此可以使用每个枚举的哈希值及其属性Set
唯一存储在Set
。 筛选器的选择存储在Set
包含MovieFilter
的实例属性下。
为了与其他对象通信,使用了FilterListControllerDelegate
的delegate
模式。 委托有3种实现方法:
- 选择过滤器。
- 取消选择过滤器。
- 清除所有选定的过滤器。
集成在Container View Controller中
在ContainerViewController
,我们有几个实例属性:
-
FilterListContainerView
和MovieListContainerView
:将用于添加子视图控制器的容器视图。 -
FilterListViewController
和MovieListViewController
:对将使用情节提要ID实例化的电影列表和滤镜列表视图控制器的引用。 -
movie
:使用默认的硬编码电影实例化的Movie
数组。
当viewDidLoad
被调用时,我们调用该方法来设置子视图控制器。 它执行以下几个任务:
- 使用情节提要ID实例化
FilterListViewController
和MovieListViewController
; - 将它们分配给实例属性;
- 将
MovieListViewController
分配给MovieListViewController
; - 将
ContainerViewController
分配为FilterListViewController
的delegate
,以便它可以响应过滤器的选择; - 设置子视图框架,并使用辅助方法扩展将它们添加为子视图控制器。
对于FilterListViewControllerDelegate
实现,选择或取消选择过滤器时,将针对每种流派,等级和持续时间过滤默认的Movies数据。 然后,将过滤器的结果分配给MovieListViewController
的movies
属性。 对于所有过滤器的取消选择,它仅分配默认的电影数据。
通过查看示例项目,我们可以看到在我们的应用程序中使用View Controller Containment和Child View Controller的好处。 我们可以将单个View Controller的职责划分为仅具有单个职责(SRP)的单独View Controller。 我们还需要确保子视图控制器对父视图控制器一无所知。 为了使子视图控制器能够与父视图通信,我们可以使用委托模式。
这种方法还提供了松散耦合模块的优势,可以带来更好的可重用性和每个组件的测试。 随着应用程序变得越来越大,越来越复杂,它确实有助于我们扩展应用程序。 让我们继续向所有人学习📖,圣诞快乐🎄和新年快乐🎊! 跟着可可继续前进!!😋
在社交媒体平台上关注我们:
面子书:facebook.com/AppCodamobile/
Twitter:twitter.com/AppCodaMobile
Instagram的:instagram.com/AppCodadotcom