1.View Controller 基础
1.1 View Controller 分类
ViewController分为container view controller 和content view controller两种类型。
这两种类型只是用途不同,前者作为一个容器容纳其他的view controller,后者用来显示内容,代码上面区别不大。前者包括UINavigationViewController、 UITabBarViewController等,后者包括UITabViewController等。对于container而言,childViewControllers数组存放了所有的childViewController。viewController的父子关系与view的父子关系不一样,以UITabBarViewController为例,它可以有两个childViewController,但这两个childViewController的view,其subview并不是UITabBarViewController的view的sub view。
1.2显示一个ViewController的内容有以下几种方法
(1)将controller作为window的root controller
(2)将controller作为一个container controller的孩子
(3)present
不应该直接的将一个controller的view,放到另一个controller的子view里面,这样会引起问题。
2. 在App中使用View Controller
2.1 使用Story Board
2.2 用编程的方式显示View Controller的内容
3.创建自定义的Content View Controller
3.1 使用Storyboard来实现自己的View Controller
当使用Storyboard时:
(1)iOS自动初始化你的view controller
(2)需要重载awakeFromNib来完成初始化
(3)在IB里面创建view体系已经其他相关联对象。
当使用编程方式来设计view controller时
(1)使用alloc 和init对view controller进行初始化
(2)创建自定义的初始化函数进行初始化
(3)重载loadView来创建和配置view体系。
4.View Controller资源管理
4.1 初始化View Controller
4.1.1 初始化来自Storyboard的View Controller
执行顺序为
- (id)initWithCoder:(NSCoder *)decoder
- (void)awakeFromNib
4.1.2 使用编程方式初始化View Controller
使用自定义的初始化函数来进行初始化,该函数应该调用super 的init函数
4.2 当View Controller的view被访问时,View Controller初始化它的view体系
执行顺序为:
1. view controller调用它的loadView函数。在默认的loadView函数中,若view controller与storyboard是关联的,loadView函数从storyboard中创建view对象,若view controller不与storyboard关联,loadView函数创建一个UIView对象,并赋值给controller的view属性。
2. view controller调用它的viewDidLoad函数,从而允许子类做一些额外的加载工作。
4.2.1 从storyboard中加载Controller的view对象
4.2.2 用编程方式创建view对象
如果不用storyboard来创建Controller的view对象,那么应该重载viewController的loadView方法,以下面的步骤来实现:
(1)创建view controller的root view对象。
(2)创建子view,并把他们add到root view中
(3)实现viewWillLayoutSubview和viewDidLayoutSubview来调整subviews的size
(4)将root view赋值给viewController的view属性
4.3 有效的管理内存
在iOS 6之后 ,在didReceiveMemoryWarning函数中将手动self.view赋值为ni来释放内
在iOS 6以前, 系统会在调用了didReceiveMemoryWarning之后将view释放,并且调用viewWillUnload 和viewDidUnload函数 。
5.对显示有关的通知进行响应
当view显示出来之前,viewWillAppear会被调用
当view显示出来之后,viewDidAppear会被调用
可以在appear函数 中使用isBeingPresented可以判断该viewController是否是被present出来,使用isMovingToParentViewController函数可以判断该viewController是否因为被作为孩子加到一个容器controller里面而显示出出来。
当view消失之前,viewWillDisappear会被调用
当view消失之后’,viewDidDisappear会被调用
在disappear函数中使用isBeingDismissed可以判断该viewController是否是被dismiss掉,使用isMovingFromParentViewController来判断是否是从parent view controller中移除。
6.改变View Controller的view的大小
6.1 view controller是怎样参与到view 布局过程中的
当view controller的view的size 改变时,它的subviews将被重新定位来适应新的空间。在view的size改变时,以下过程将发生:
(1)view controller的view 被改变为新的size
(2)如果没有用auto layout,subviews根据它们的autoresizing mask来改变大小。
(3)view controller的viewWillLayoutSubviews被调用
(4)view controller的view的layoutSubviews被调用,如果使用了auto layout,…
(5)viewController的viewDidLayoutSubviews被调用
理想情况下,subviews们自己执行了重定位相关的工作,如果需要自己调整subviews的位置,可以重载layoutSubview来调整那些不能被通过resizing mask自动定位的sub view。
7.使用响应者链中的View Controller
8.支持多个方向
8.1 iOS6以后的处理方式
当UIKit收到一个转向的通知时,它使用UIApplication 对象和app的root view controller来决定是否支持新的方向。如果两个对象都同意新的方向,则转向,否则通知被忽略。
当有一个viewController被present出来之后,这个被present出来的view controller被用来代替root view controller来决定是否转向,同时,这个presented controller可以使用一个预置的方向。
view controller使用函数- (NSUInteger)supportedInterfaceOrientations来声明支持的方向,使用preferredInterfaceOrientationForPresentation函数来声明默认的presented controller的方向。
如果希望暂时禁用自动转向,重载最顶层的view controller的shouldAutorotate函数来返回NO,这样就不会自动转向。
8.2 iOS6以前的处理方式
使用的函数不一样,但机制一样,都是基于root view controller。
- (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)orientation
{
if ((orientation == UIInterfaceOrientationPortrait) ||
(orientation == UIInterfaceOrientationLandscapeLeft))
return YES;
return NO;
}
8.3 在当前显示的view controller中响应方向改变
当设备方向改变时,当前显示的view controller是会收到通知,并且被给予一个机会来执行额外的任务。可以使用这些方法来显示或隐藏views,重新定位views等等,但不要在此时执行耗时操作。比较合适的处理是提供独特的views来适应不同方向,比如弹出一个新的view controller。
通知消息会被发送给root view controller,root view controller将这些events在需要的时候传递给它的孩子,以下是旋转发送时事件的顺序。
(1)window调用root view controller的willRotateToInterfaceOrientation:duration:函数。Container view controllers会降该消息发送给当前显示的content view controller。可以在自己的content view controller里面重载该函数来隐藏某些view或者做些改变。
(2)window调整view controller的view bound。这会导致view 重新布局subviews,触发controller的viewWillLayoutSubviews 函数。当这个函数运行时,可以根据app的 statusBarOrientation属性来判断当前的方向。
(3) view controller的willAnimateRotationToInterfaceOrientation:duration:函数被调用
(4)发生动画
(5) didRotateFromInterfaceOrientation: 被调用。container view controller会发送该消息给当前显示的content view controller。
8.4 创建一个可选的横屏界面
如果希望在横屏时提供基于相同数据的不同界面,最好的方式是使用两个单独的controllers。一个管理横向界面,一个管理竖向界面。
实现方法为:
(1)实现两个view controller,一个只支持横屏,一个只支持竖屏
(2)注册UIDeviceOrientationDidChangeNotification 消息,在handler方法里面,基于当前的方向,present或者dismiss可选的view controller。
8.5 Tips for Implementing Your Rotation Code
9.Presenting View Controllers
10.在View Controllers之间进行协调
10.1.使用delegation与其他controller通信
10.2 管理controller 数据的指导方针
(1)一个目标view controller引用的数据应该来自其源view controller,除非它是一个自我配置的view controller
(2)总是使用delegation将信息返回其他controller,content view controller永远不需要知道源view controller或者不是由它创建出来view controller的信息
(3)避免不必要的连接到外部的view controller,每个连接代表的依赖,使得它更难改变你的应用程序的设计。
11.使用View Controller的 编辑模式
12.创建自定义Segue
13.创建自定义Container View Controller
13.1 设计自己的Container View Controller
13.2 通用Container View Controller范例
13.3 实现自定义Container View Controller
实现一个容器的目标是能够将其他view controller的view作为子树增加到当前容器controller的view体系中。当增加一个孩子controller的view时,你需要确保事件会被分发到相应的controller中,因此需要显式的将新的controller作为容器controller的的孩子。
13.3.1 增加或者移除一个孩子
(1)增加controller的例子
步骤为:
调用 addChildViewController:将新的controller加到容器的孩子中,该函数会自动调用新controller的 willMoveToParentViewController函数
设置新controller的view的frame
将新的controller的view加到container的view体系中(不一定是container的root view)
调用新controller的didMoveToParentViewController函数
范例代码为:
- (void) displayContentController: (UIViewController*) content;
{
[self addChildViewController:content]; // 1
content.view.frame = [self frameForContentController]; // 2
[self.view addSubview:self.currentClientView];
[content didMoveToParentViewController:self]; // 3
}
(2)移除controller的孩子
步骤为:
调用孩子的willMoveToParentViewController:
移除view
调用孩子的removeFromParentViewController ,将孩子从容器中移除,该函数会自动调用didMoveToParentViewController:
范例代码为:
- (void) hideContentController: (UIViewController*) content
{
[content willMoveToParentViewController:nil]; // 1
[content.view removeFromSuperview]; // 2
[content removeFromParentViewController]; // 3
}
以下代码演示了在两个viewController之间进行切换:
- (void) cycleFromViewController: (UIViewController*) oldC
toViewController: (UIViewController*) newC
{
[oldC willMoveToParentViewController:nil]; // 1
[self addChildViewController:newC];
newC.view.frame = [self newViewStartFrame]; // 2
CGRect endFrame = [self oldViewEndFrame];
[self transitionFromViewController: oldC toViewController: newC // 3
duration: 0.25 options:0
animations:^{
newC.view.frame = oldC.view.frame; // 4
oldC.view.frame = endFrame;
}
completion:^(BOOL finished) {
[oldC removeFromParentViewController]; // 5
[newC didMoveToParentViewController:self];
}];
}
13.3.2 自定义外观和旋转回调行为
13.3.2 建立容器controller的实际建议