在每个 iOS 开发者的生涯中,总有一些时候想把一个视图控制器放到一个 tableView
的 cell 中。因为这是一个有用的工具去处理我在视图控制器中的各种复杂视图及繁琐操作,而且很容易想象的一种情况是你想要将一些视图堆在另一些视图上面。另一个常见的应用场景是将 collectionView
放在 cell 里。理想情况下里面的 collectionView
拥有它自己的控制器,这样外面的 tableView
控制器不会受到关联视图和每个 collection view cell 数据的影响。
因为 UIKit 有很多 hook(钩子函数)方法,用于组件之间于幕后相互通信,我们需要确保使用 UIViewController 容器 API 管理子视图控制器,如果不这样做可能会不知所以地失败或者表现不正常。
在这篇文章中我主要谈论 tableView
和它们的 cell,但是这些方法也适用于 collectionView
。
为了简单起见,让视图控制器是 cell 中的唯一内容。相比管理单个视图控制器的根视图,尝试去管理一堆常规的视图反而会产生不必要的复杂。使用一个视图控制器并且每个 cell 中仅有一个视图控制器,布局(在 cell 层级)像下面这样简单
1 |
self.viewController.view.frame = self.contentView.bounds; |
视图控制器能够内在处理自身的布局。我们也可以把高度的计算也放视图控制器里面。
这里有两种实现方法:可以每个 cell 持有一个视图控制器,也可以在控制器层管理这些视图控制器。
每个 cell 都持有视图控制器
如果我们在每个 cell 中放一个视图控制器,我们可以在这个 cell 中懒加载它。
1 2 3 4 5 6 7 |
- (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 上.
1 2 3 4 5 6 7 |
- (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 的类中,实现这些方法来生成简单的视图控制器容器。
1 2 3 4 5 6 7 8 9 10 11 |
- (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。现在我们的设备有更多的内存,所以如果当你只需要展示很少的行时可能不需要重用视图。
1 2 3 4 5 6 7 8 9 10 11 12 |
- (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/span>*)childController { [self addChildViewController有一些时候想把一个视图控制器放到一个 tableView 的 cell 中。因为这是一个有用的工具去处理我在视图控制器中的各种复杂视图及繁琐操作,而且很容易想象的一种情况是你想要将一些视图堆在另一些视图上面。另一个常见的应用场景是将 collectionView 放在 cell 里。理想情况下里面的 collectionView 拥有它自己的控制器,这样外面的 tableView 控制器不会受到关联视图和每个 collection view cell 数据的影响。
因为 UIKit 有很多 hook(钩子函数)方法,用于组件之间于幕后相互通信,我们需要确保使用 UIViewController 容器 API 管理子视图控制器,如果不这样做可能会不知所以地失败或者表现不正常。 在这篇文章中我主要谈论 为了简单起见,让视图控制器是 cell 中的唯一内容。相比管理单个视图控制器的根视图,尝试去管理一堆常规的视图反而会产生不必要的复杂。使用一个视图控制器并且每个 cell 中仅有一个视图控制器,布局(在 cell 层级)像下面这样简单
视图控制器能够内在处理自身的布局。我们也可以把高度的计算也放视图控制器里面。 这里有两种实现方法:可以每个 cell 持有一个视图控制器,也可以在控制器层管理这些视图控制器。 每个 cell 都持有视图控制器如果我们在每个 cell 中放一个视图控制器,我们可以在这个 cell 中懒加载它。
记住我们不是将这个视图控制器的根视图作为一个子视图加入到我们 cell 的
在 cell 的类中,实现这些方法来生成简单的视图控制器容器。
在视图控制器作为子视图控制器加入之后,将 subview 加到视图中,确认 在父容器中持有视图控制器每个 cell 有它自己的视图控制器能够正常工作,但是感觉有点怪异。在 Cocoa 的 MVC 模式中,模型和视图不应该知道它们所用的视图控制器,让一个 cell(实际上是一个 我们有两个方法来实现这个任务,(比较简单的方法是)我们可以为我们需要展示的表格中的每个 item 预生成一个视图控制器(和一个 view),或者在需要的时候生成视图控制器,然后循环使用它们,就像
|