在每个 iOS 开发者的生涯中,总有一些时候想把一个视图控制器放到一个 tableView 的 cell 中。因为这是一个有用的工具去处理我在视图控制器中的各种复杂视图及繁琐操作,而且很容易想象的一种情况是你想要将一些视图堆在另一些视图上面。另一个常见的应用场景是将 collectionView 放在 cell 里。理想情况下里面的 collectionView 拥有它自己的控制器,这样外面的 tableView 控制器不会受到关联视图和每个 collection view cell 数据的影响。

因为 UIKit 有很多 hook(钩子函数)方法,用于组件之间与幕后相互通信,我们需要确保使用UIViewController 容器 API 管理子视图控制器,如果不这样做可能会不知所以地失败或者表现不正常。

在这篇文章中我主要谈论 tableView 和它们的 cell,但是这些方法也适用于 collectionView。

为了简单起见,让视图控制器是 cell 中的唯一内容。相比管理单个视图控制器的根视图,尝试去管理一堆常规的视图反而会产生不必要的复杂。使用一个视图控制器并且每个 cell 中仅有一个视图控制器,布局(在 cell 层级)像下面这样简单

self.viewController.view.frame = self.contentView.bounds;

视图控制器能够内在处理自身的布局。我们也可以把高度的计算也放视图控制器里面。

这里有两种实现方法:可以每个 cell 持有一个视图控制器,也可以在控制器层管理这些视图控制器。

每个 cell 都持有视图控制器

如果我们在每个 cell 中放一个视图控制器,我们可以在这个 cell 中懒加载它。

- (SKContentViewController *)contentViewController {

if (!_contentViewController) {

SKViewController *contentViewController = [[SKContentViewController alloc] init];

self.contentViewController = contentViewController;

}

return _contentViewController;

}

记住我们不是将这个视图控制器的根视图作为一个子视图加入到我们 cell 的 contentView。当在 -cellForRowAtIndexPath: 方法中需要配置这个 cell 时,我们可以将我们的 model 传入到这个控制器,然后它会根据最新的内容配置自己。由于这些 cell 是复用的,你的控制器必须设计为在任何时候只要它的 model 改变就会完全地重置它自己。

UITableView 给我们了 cell 显示前后和移除前后的 hooks。我们想要在这个时候将 cell 的视图控制器加到我们的父表格视图控制器并且把 cell 视图控制器的根视图加到 cell 上.

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {

[cell addViewControllerToParentViewController:self];

}

- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {

[cell removeViewControllerFromParentViewController];

}

在 cell 的类中,实现这些方法来生成简单的视图控制器容器。

- (void)addViewControllerToParentViewController:(UIViewController *)parentViewController {

[parentViewController contentViewController];

[self.contentViewController didMoveToParentViewController:parentViewController];

[self.contentView addSubview:self.contentViewController.view];

}

- (void)removeViewControllerFromParentViewController {

[self.contentViewController.view removeFromSuperview];

[self.contentViewController willMoveToParentViewController:nil];

[self.contentViewController removeFromParentViewController];

}

在视图控制器作为子视图控制器加入之后,将 subview 加到视图中,确认 -viewWillAppear: 之类的方法能被正确的调用。tableView 的 willDisplayCell: 方法与显示方法(-viewWillAppear: 和 -viewDidAppear:)是对应的,并且 -didEndDisplayingCell: 方法与消失方法相对应,因此我们就可以在这些方法展示我们的容器了。

在父容器中持有视图控制器

每个 cell 有它自己的视图控制器能够正常工作,但是感觉有点怪异。在 Cocoa 的 MVC 模式中,模型和视图不应该知道它们所用的视图控制器,让一个 cell(实际上是一个 UIView)持有一个控制器违反了这个规则体系。为了解决这个问题,我们可以在表格视图控制器中,在父容器级别持有所有子视图控制器。

我们有两个方法来实现这个任务,(比较简单的方法是)我们可以为我们需要展示的表格中的每个 item 预生成一个视图控制器(和一个 view),或者在需要的时候生成视图控制器,然后循环使用它们,就像 UITableView 对 cell 的重用一样(这个比较困难)。首先从简单的方式开始,当 iPhone 刚出现的时候,设备受内存的限制不能为表格中的每行生成一个 view。现在我们的设备有更多的内存,所以如果当你只需要展示很少的行时可能不需要重用视图。

- (void)setupChildViewControllers {

self.contentViewControllers = [self.modelObjects arrayByTransformingObjectsUsingBlock:^id(SKModel *model) {

SKViewController *contentViewController = [[SKContentViewController alloc] initWithModel:model];

[self addChildContentViewController:contentViewController];

return contentViewController;

}];

}

- (void)addChildContentViewController:(UIViewController *)childController {

[self addChildViewController:childController];

[childController didMoveToParentViewController:self];

}

(上面我使用了 Objective-Shorthand 中的 -arrayByTransformingObjectsUsingBlock: 方法,也就是所谓的 -map:)

一旦你有了视图控制器,你可以在方法 -cellForRowAtIndexPath: 中把它们的视图放到 cell 里

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

UITableViewCell *cell = //make a cell

SKViewController *viewController = self.contentViewControllers[indexPath.row];

cell.hostedView = contentViewController.view;

return cell;

}

在 cell 里面,你可以拿到这个 hostedView,将它加到子视图中,并且在重用的时候清除它。

- (void)setHostedView:(UIView *)hostedView {

_hostedView = hostedView;

[self.contentView addSubview:hostedView];

}

- (void)prepareForReuse {

[super prepareForReuse];

[self.hostedView removeFromSuperview];

self.hostedView = nil;

}

这就是简单的方法你要做的所有事情。为了将视图控制器用于重用,需要一个 NSMutableSet 类型的 unusedViewControllers 和一个 NSMutableDictionary 类型的viewControllersByIndexPath.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

UITableViewCell *cell = //make a cell

SKViewController *viewController = [self recycledOrNewViewController];

viewController.model = [self.dataSource objectAtIndexPath:indexPath];

self.viewControllersByIndexPath[indexPath] = viewController;

cell.hostedView = contentViewController.view;

return cell;

}

- (UIViewController *)recycledOrNewViewController {

if (self.unusedViewControllers.count > 1) {

UIViewController *viewController = [self.unusedViewControllers anyObject];

[self.unusedViewControllers removeObject:viewController];

return viewController;

}

SKViewController *contentViewController = [[SKContentViewController alloc] init];

[self addChildViewController:contentViewController];

return contentViewController;

}

- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {

UIViewController *viewController = self.viewControllersByIndexPath[indexPath];

[self.viewControllersByIndexPath removeObjectForKey:indexPath]

[self.unusedViewControllers addObject:viewController];

}

- (NSMutableSet *)unusedViewControllers {

if (!_unusedViewControllers) {

self.unusedViewControllers = [NSMutableSet set];

}

return _unusedViewControllers;

}

- (NSMutableDictionary *)viewControllersByIndexPath {

if (!_viewControllersByIndexPath) {

self.viewControllersByIndexPath = [NSMutableDictionary dictionary];

}

return _viewControllersByIndexPath;

}

有三件重要的事情,一,unusedViewControllers 里面装的是所有等待重用的视图控制器,二,viewControllersByIndexPaths 里面装的是所有正在用的视图控制器(我们必须持有它们,否则将会被销毁)。最后,cell 只与视图控制器的 hostedView 接触,符合我们之前的视图不能知道视图控制器原则。

这是我发现的适用于将 UIViewController 对象放入cell中的两个最好方法。如果我漏掉了任何技术我很乐意倾听。

英文出处:khanlou.com

本文由 伯乐在线 - Above 翻译,nathanw 校稿

译文链接:http://ios.jobbole.com/82123/

转载于:https://www.cnblogs.com/xvewuzhijing/p/5003877.html

iOS开发学无止境 - Cell 里的视图控制器相关推荐

  1. iOS开发 剖析网易新闻标签栏视图切换(addChildViewController属性介绍)

    iOS开发 剖析网易新闻标签栏视图切换(addChildViewController属性介绍) 本来仅仅是打算介绍一下addChildViewController这种方法的,正好今天朋友去换工作面试问 ...

  2. IOS开发入门(11)-导航控制器(1)

    IOS开发入门(11)-导航控制器I:层级结构和标签 前言:(直接从书上抄的) 大多数应用程序是由主视图导出多个屏幕,并且通常情况下实现屏幕切换的方法还不止一种.我们需要一种方式来实现用户在应用程序内 ...

  3. ios在判断设备是ipad_Swift - 判断设备类型开发兼容的iOS应用(iPad使用分隔视图控制器)...

    1.分割视图控制器(UISplitViewController) 在iPhone应用中,使用导航控制器由上一层界面进入下一层界面. 但iPad屏幕较大,通常使用SplitViewController来 ...

  4. iOS开发系列课程(10) --- 表格视图

      UITableView(表格视图)是iOS应用程序开发中应用最广泛的一个控件,几乎十有八九的应用程序中都会用到它,它是基于滚动视图的列表互动类.使用UITableView可以在屏幕上显示单元格的列 ...

  5. iOS开发之创建颜色渐变视图View

    在iOS开发中有时需要自己自定义一个视图view的背景,而网上有人提出的在循环中不断alloc的方法设置其背景色渐变,会耗费很多内存和资源,极其不明智,而在CALayer中早就提供有图层渐变的类和相应 ...

  6. iOS 获取当前正在显示的视图控制器ViewController(最全)

    目录 先插入以下介绍以下本编文章除标题所讲知识外,还额外包含的内容有如下: 1.获取当前显示的视图控制器ViewController 2.如何在多次presentViewController后直接返回 ...

  7. iOS开发学无止境 - 6个iOS图片文本设计的小技巧

    英文:TOPE 作者:星夜暮晨 网址:http://www.jianshu.com/p/88263196fdf0 设计师们似乎拥有着我们这些开发者所没有的"魔力",他们知道如何让一 ...

  8. iOS开发学无止境 - NSFileManager文件操作的十个小功能

    (配图的小故事还记得嘛) NSFileManager是一个单列类,也是一个文件管理器.可以通过NSFileManager创建文件夹.创建文件.写文件.读文件内容等等基本功能. 下面将介绍NSFileM ...

  9. iOS开发学无止境 - 异步图片加载优化与常用开源库分析

    作者:罗轩(@luoyibu) 网址:http://www.jianshu.com/p/3b2c95e1404f 1. 网络图片显示大体步骤:   下载图片 图片处理(裁剪,边框等) 写入磁盘 从磁盘 ...

最新文章

  1. SQLite.swift的简单使用
  2. 解决IDEA中导入新的maven依赖后Language Level自动重置问题
  3. 皮一皮:可怜的西瓜...
  4. str.endswith可以传入集合数据类型,而不仅仅是字符串
  5. 自动化测试框架:自动化测试呼唤开发
  6. linux删除第二次出现的字符,linux下 怎样删除文件名中包含特殊字符的文件
  7. numpy支持比python更多的数据类型_NumPy数据类型
  8. 知识星球如何升级为正式星球 知识星球怎么创建星球
  9. Real Application Cluster 10g安装与配置(下)
  10. web自动化测试python+selenium学习总结----selenium安装、浏览器驱动下载
  11. Guava学习笔记(三):Preconditions优雅的检验参数
  12. SQL语句基本用法格式
  13. 数字图像处理边缘检测算子matlab,使用roberts算子对图像进行边缘检测,并二值化的matlab代码实现...
  14. 塔康信号matlab,干扰效能仿真,jamming effect simulation,音标,读音,翻译,英文例句,英语词典...
  15. duilib库combo box提供输入字符模糊查询
  16. 《流浪方舟》- 废土世界的冒险之旅
  17. 网络姻缘一线牵 大数据精准推广才能让合适的产品遇到合适的人!
  18. wordpress插件_您应该知道的2018年10个高级WordPress插件
  19. UnityShader(三)基础多光照+遮罩Shader
  20. 无线射频芯片CC2540F256RHAR 中文资料介绍

热门文章

  1. Python from import导包ModuleNotFoundError No module named,找不到模块问题
  2. java计算机毕业设计的洗衣店订单管理系统源码+数据库+系统+lw文档+mybatis+运行部署
  3. 2016百度之星 初赛(Astar Round2B) 1001 区间的价值
  4. 买零食 HihoCoder - 1272 (大爆搜DFS)
  5. p2p网络实现(C++)
  6. html标签语义化的好处,(2019)[前端]面试题[9]:HTML5语义化标签和新特性
  7. mysql 烂泥_烂泥:mysql帮助命令使用说明
  8. 从一文中了解SSRF的各种绕过姿势及攻击思路
  9. [重构]1.关于提炼方法,变量,类
  10. 百亿互刷宝php 站长,百度排名百亿互刷宝