View Controller容器
在 iOS 5 之前,view controller 容器是 Apple 的特权。实际上,在 view controller 编程指南中还有一段申明,指出你不应该使用它们。Apple 对 view controllers 的总的建议曾经是“一个 view controller 管理一个全屏幕的内容”。这个建议后来被改为“一个 view controller 管理一个自包含的内容单元”。为什么 Apple 不想让我们构建自己的 tab bar controllers 和 navigation controllers?或者更确切地说,这段代码有什么问题:
[viewControllerA.view addSubView:viewControllerB.view]

UIWindow 作为作为一个应用程序的根视图(root view),是旋转和初始布局消息等事件产生的来源。在上图中,child view controller 的 view 插入到 root view controller 的视图层级中,被排除在这些事件之外了。View 事件方法诸如 viewWillAppear:将不会被调用。
在 iOS 5 之前构建自定义的 view controller 容器时,要保存一个 child view controller 的引用,还要手动在 parent view controller 中转发所有 view 事件方法的调用,要做好非常困难。
一个例子
当你还是个孩子,在沙滩上玩时,你父母是否告诉过你,如果不停地用铲子挖,最后会到达美国?我父母就说过,我就做了个叫做Tunnel 的 demo 程序来验证这个说法。你可以 clone 这个 Github 代码库并运行这个程序,它有助于让你更容易理解示例代码。(剧透:从丹麦西部开始,挖穿地球,你会到达南太平洋的某个地方)

为了寻找对跖点,也称作相反的坐标,将拿着铲子的小孩四处移动,地图会告诉你对应的出口位置在哪里。点击雷达按钮,地图会翻转过来显示位置的名称。
屏幕上有两个 map view controllers。每个都需要控制地图的拖动,标注和更新。翻过来会显示两个新的 view controllers,用来检索地理位置。所有的 view controllers 都包含于一个 parent view controller 中,它持有它们的 views,并保证正确的布局和旋转行为。
Root view controller 有两个 container views。添加它们是为了让布局,以及 child view controllers 的 views 的动画做起来更容易,我们马上就可以看到。
- (void)viewDidLoad
{
[super viewDidLoad];
//Setup controllers
_startMapViewController = [RGMapViewController new];
[_startMapViewController setAnnotationImagePath:@"man"];
[self addChildViewController:_startMapViewController]; // 1
[topContainer addSubview:_startMapViewController.view]; // 2
[_startMapViewController didMoveToParentViewController:self]; // 3
[_startMapViewController addObserver:self
forKeyPath:@"currentLocation"
options:NSKeyValueObservingOptionNew
context:NULL];
_startGeoViewController = [RGGeoInfoViewController new]; // 4
}
我们实例化了 _startMapViewController,用来显示起始位置,并设置了用于标注的图像。
_startMapViewcontroller被添加成 root view controller 的一个 child。这会自动在 child 上调用willMoveToParentViewController:方法。- child 的 view 被添加成 container view 的 subview。
- child 被通知到它现在有一个 parent view controller。
- 用来显示地理位置的 child view controller 被实例化了,但是还没有被插入到任何 view 或 controller 层级中。
布局
Root view controller 定义了两个 container views,它决定了 child view controller 的大小。Child view controllers 不知道会被添加到哪个容器中,因此必须适应大小。
- (void) loadView
{
mapView = [MKMapView new];
mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[mapView setDelegate:self];
[mapView setMapType:MKMapTypeHybrid];
self.view = mapView;
}
现在,它们就会用 super view 的 bounds 来进行布局。这样增加了 child view controller 的可复用性;如果我们把它 push 到 navigation controller 的栈中,它仍然会正确地布局。
过场动画
Apple 已经针对 view controller 容器做了细致的 API,我们可以构造我们能想到的任何容器场景的动画。Apple 还提供了一个基于 block 的便利方法,来切换屏幕上的两个 controller views。方法 transitionFromViewController:toViewController:(...)已经为我们考虑了很多细节。
- (void) flipFromViewController:(UIViewController*) fromController
toViewController:(UIViewController*) toController
withDirection:(UIViewAnimationOptions) direction
{
toController.view.frame = fromController.view.bounds; // 1
[self addChildViewController:toController]; //
[fromController willMoveToParentViewController:nil]; //
[self transitionFromViewController:fromController
toViewController:toController
duration:0.2
options:direction | UIViewAnimationOptionCurveEaseIn
animations:nil
completion:^(BOOL finished) {
[toController didMoveToParentViewController:self]; // 2
[fromController removeFromParentViewController]; // 3
}];
}
- 在开始动画之前,我们把
toController作为一个 child 进行添加,并通知fromController它将被移除。如果fromController的 view 是容器 view 层级的一部分,它的viewWillDisapear:方法就会被调用。 toController被告知它有一个新的 parent,并且适当的 view 事件方法将被调用。fromController被移除了。
这个为 view controller 过场动画而准备的便捷方法会自动把老的 view controller 换成新的 view controller。然而,如果你想实现自己的过场动画,并且希望一次只显示一个 view,你需要在老的 view 上调用 removeFromSuperview,并为新的 view 调用addSubview:。错误的调用次序通常会导致 UIViewControllerHierarchyInconsistency 警告。例如:在添加 view 之前调用didMoveToParentViewController: 就触发这个警告。
为了能使用 UIViewAnimationOptionTransitionFlipFromTop 动画,我们必须把 children's view 添加到我们的 view containers 里面,而不是 root view controller 的 view。否则动画将导致整个 root view 都翻转。
通信
View controllers 应该是可复用的、自包含的实体。Child view controllers 也不能违背这个经验法则。为了达到目的,parent view controller 应该只关心两个任务:布局 child view controller 的 root view,以及与 child view controller 暴露出来的 API 通信。它绝不应该去直接修改 child view tree 或其他内部状态。
Child view controller 应该包含管理它们自己的 view 树的必要逻辑,而不是把它们看作单纯呆板的 views。这样,就有了更清晰的关注点分离和更好的可复用性。
在示例程序 Tunnel 中,parent view controller 观察了 map view controllers 上的一个叫 currentLocation 的属性。
[_startMapViewController addObserver:self
forKeyPath:@"currentLocation"
options:NSKeyValueObservingOptionNew
context:NULL];
当这个属性跟着拿着铲子的小孩的移动而改变时,parent view controller 将新坐标的对跖点传递给另一个地图:
[oppositeController updateAnnotationLocation:[newLocation antipode]];
类似地,当你点击雷达按钮,parent view controller 给新的 child view controllers 设置待检索的坐标。
[_startGeoViewController setLocation:_startMapViewController.currentLocation];
[_targetGeoViewController setLocation:_targetMapViewController.currentLocation];
我们想要达到的目标和你选择的手段无关,从 child 到 parent view controller 消息传递的技术,不论是采用 KVO,通知,或者是委托模式,child view controller 都应该独立和可复用。在我们的例子中,我们可以将某个 child view controller 推入到一个 navigation 栈中,它仍然能够通过相同的 API 进行通信。
转自:http://objccn.io/issue-1-4/
View Controller容器的更多相关文章
- iOS Container View Controller
一.UIViewController 做iOS开发的经常会和UIViewController打交道,从类名可知UIViewController属于MVC模型中的C(Controller),说的更具体点 ...
- View Controller Programming Guide for iOS---(一)---About View Controllers
About View Controllers View controllers are a vital link between an app’s data and its visual appear ...
- iOS 容器控制器 (Container View Controller)
iOS 容器控制器 (Container View Controller) 一个控制器包含其他一个或多个控制器,前者为容器控制器 (Container View Controller),后者为子控制器 ...
- iOS架构师之路:控制器(View Controller)瘦身设计
前言 古老的MVC架构是容易被iOS开发者理解和接受的设计模式,但是由于iOS开发的项目功能越来越负责庞大,项目代码也随之不断壮大,MVC的模糊定义导致我们的业务开发工程师很容易把大量的代码写到视图控 ...
- 【IOS笔记】View Controller Basics
View Controller Basics 视图控制器基础 Apps running on iOS–based devices have a limited amount of screen s ...
- View Controller 视图管理总结
View controller是iOS中顶层的视图载体和控制器,它需要对view负责,管理view的生命周期,相关处室话以及交互事件,除此以外还需要为view提供合适的数据,以供view使用. Vie ...
- View Controller Transition:京东加购物车效果
冬天已经过去了,阳光越来越暖洋洋的了.还记得上学的时候,老师总说"春天是播种的季节",而我还没在朋友圈许下什么愿望.一年了,不敢想象回首还能看到点什么,所以勇往直前.当被俗世所扰, ...
- 自己定义View Controller转换动画
原文链接 : Introduction to Custom View Controller Transitions and Animations 原文作者 : joyce echessa 译文出自 : ...
- Swift:超炫的View Controller切换动画
匿名社交应用Secret的开发者开发了一款叫做Ping的应用,用户可以他们感兴趣的话题的推送. Ping有一个很炫的东西,就是主界面和之间切换的动画做的非常的好.每次看到一个非常炫的动画,都不由得会想 ...
随机推荐
- POJ 2549:Subsets(哈希表)
[题目链接] http://poj.org/problem?id=2549 [题目大意] 给出一个数集,从中选择四个元素,使得a+b+c=d,最小化d [题解] 我们对a+b建立Hash_table, ...
- 第七章 android-UI组件
一.本章目录 二.用户界面概述 1,用户界面简介 (1)系统和用户之间进行信息交换的媒介 2,设计手机用户界面应解决的问题 (1)需要界面设计和逻辑代码完全分离(布局和逻辑代码分开放) (2)根据不同 ...
- apache去掉目录浏览
apache去掉目录浏览 apache默认开启目录浏览的,这样大大降低了我们网站的安全,下面是关闭浏览目录: 要禁止 Apache 显示目录结构列表,只需将 Option 中的 Indexes 去掉即 ...
- EasyMvc入门教程
EasyMvc 希望实现的目标:模块化,快速简单化,满足80%的常见需求.基于.Net Core 2.0.5开发.开发环境:VS2017,运行环境支持Window/Linux. 相关链接: 演示地址: ...
- SilverLight-3:目录
ylbtech-SilverLight-Index: 1.A,返回顶部 Layout The Layout Containers - The Panel Background Borders Si ...
- 可以使用foreach遍历循环的条件
大话C#中能使用foreach的集合的实现 转自:http://www.cnblogs.com/tangzhengyue/p/3339936.html 大家都知道foreach的语法: forea ...
- hadoop错误总结
1.hadoop3: mkdir: cannot create directory `/usr/local/hadoop/bin/../logs': Permission denied 把所有Data ...
- Docker以及registry的入门学习安装
一.前言 如果你是数据中心或云计算IT圈子的人,我想你一定听过Docker,关于它们的新闻从未间断过.Docker的发展历程虽然算不上太长,但是自2014年6月Docker 1.0 正式发布,但是Do ...
- 使navicat可以通过SSH连接MySQL数据库
1.编辑/etc/ssh/sshd_config,在最下面添加如下语句 KexAlgorithms diffie-hellman-group1-sha1,curve25519-sha256@libss ...
- ping百度不通的解决方案
1.ping www.baidu.com unknow baidu.com 第一步,确定是否能ping通网关 第二步,确定是否能直接ping通外网 如ping 8.8.8.8 第三步,如果上面两个都 ...