初学 iOS,本文翻译了一些 iOS 官网上的 UIViewController 的知识点,如有不到位或不正确的地方,还请指正:
理解content view controllers 和 container view controllers
知道如何实现自定义view controller containers 以及 何时使用view controller containers
在iOS5操作系统下使用UIPageViewController
分享一些新的API和使用技巧
这是一个UIViewController
两个原因
设计模式
它们是MVC中的C
它们管理着“整个屏幕里显示的内容”
它们通常和它们的对象模型打包在一起
系统视图控制器通包括以下5种
TWTweetComposeViewController
UIImagePickerController
EKEventViewController
MFMailComposeViewController
MPMediaPickerController
设计模式
它们是MVC中的C
它们管理着“整个屏幕里显示的内容”
它们通常和它们的对象模型打包在一起
你的应用程序可以再各个视图控制器之间移动 (Your application flows between view controllers)
它们管理“整个屏幕里显示的内容”?
它们看起来有很灵活的边界
他们管理这各自独立的“内容单元”
Table view controller: 表视图控制器 Detail controller: 详细控制器 SVC:SSL VPN Client
根视图控制器真的可以管理“整个屏幕里显示的内容”么?
window.rootViewController= RootViewController
(void)viewWillAppear: (void)viewDidAppear: (void)viewWillDisappear: (void)viewDidDisappear:
(void)viewWillRotateToInterfaceOrientation:duration: (void)viewWillAnimateRotationToInterfaceOrientation: (void)viewDidRotateFromInterfaceOrientation:
也许在iPhone上很少要关心的屏幕旋转问题的,但是大屏幕的iPad上就不同了,很多时候你需要关心横竖屏。rotation callbacks 一般情况下只需要关心三个方法 : willRotateToInterfaceOrientation:duration:在旋转开始前,此方法会被调用; willAnimateRotationToInterfaceOrientation:duration: 此方法的调用在旋转动画block的内部,也就是说在此方法中的代码会作为旋转animation block的一部分; didRotateFromInterfaceOrientation:此方法会在旋转结束时被调用。而作为view controller container 就要肩负起旋转的决策以及旋转的callbacks的传递的责任。
总结
视图控制器仅仅只是控制器 —— 是MVC中的C
视图控制器管理视图层次结构 —— 而非单一的视图
视图控制器通常是自包含的(可重复使用)
视图控制器连接并支持通用的iOS应用程序流
了解视图控制器容器
两个层次的故事
蓝色:视图颜色 蓝色箭头:子视图关系
金色: 视图控制器颜色 灰色箭头:父视图控制器关系
Container View Controller 容器视图控制器包含其他视图控制器所拥有的内容。也就是说一个View Controller显示的某部分内容属于另一个View Controller,那么这个View Controller就是一个Container. iOS 5.0 开始支持Custom Container View Controller,开放了用于构建自定义Container的接口。如果你想创建一个自己的Container,那么有一些概念还得弄清楚。Container的主要职责就是管理一个或多个Child View Controller的展示的生命周期,需要传递显示以及旋转相关的回调。
UITableViewController 很多应用程序显示表格数据。因此,iOS提供了一个专门用来管理表格数据的内建的UIViewController类的子类。UITableViewController 管理一个表格视图并支持很多标准表格相关的行为,比如选择(selection)管理,行编辑,以及表格配置。这些额外的支持减少了你创建和初始化一个基于表格界面必须编写的代码总量。你还可以子类化UITableViewController来添加其它自定义行为。
Recipe Controller 食谱控制器
显示或者旋转的回调的触发的源头来自于window,一个app首先有一个主window,初始化的时候需要给这个主window指定一个rootViewController,window会将显示相关的回调(viewWillAppear:, viewWillDisappear:, viewDidAppear:, or viewDidDisappear: )以及旋转相关的回调(willRotateToInterfaceOrientation:duration: ,willAnimateRotationToInterfaceOrientation:duration:, didRotateFromInterfaceOrientation:)传递给rootViewController。rootViewController需要再将这些callbacks的调用传递给它的Child View Controllers。
(void)addChildViewController:(UIViewController *)childController; (void)removeFromParentViewController;
@property(nonatomic,readonly) NSArray *childViewControllers;
(void)willMoveToParentViewController:(UIViewController *)parent; (void)didMoveToParentViewController:(UIViewController *)parent;
UIViewControllerHierarchyInconsistencyException 为什么会这么糟糕?
[self addChildViewController:note]; // Transition to note controller with a flip transition which adds // tne note view to the window hierarchy and removes the recipe view. [self transitionFromViewController:recipe toViewController:note duration:.5 options:UIViewAnimationOptionTransitionFlipFromRight animations:nil completion:^(BOOL finished) { [note didMoveToParentViewController:self]; }];
viewWillAppear: 该视图添加到 windows 的视图层次结构之前调用 在 vc.view layoutSubviews 之前调用
viewDidAppear: 该视图添加到视图层次结构之后调用 在 vc.view layoutSubviews 之后调用
viewWillDisappear: 该视图从 windows 的视图层次结构移除之前调用
[self addChildViewController:note]; // Transition to note controller with a flip transition which adds // tne note view to the window hierarchy and removes the recipe view. [self transitionFromViewController:recipe toViewController:note duration:.5 options:UIViewAnimationOptionTransitionFlipFromRight animations:nil completion:^(BOOL finished) { [note didMoveToParentViewController:self]; }];
- (void)transitionFromViewController:(UIViewController *) fromVC toViewController:(UIViewController *)toVC duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion;
- (void)viewWillLayoutSubviews - (void)viewDidLayoutSubviews
流 — —打开和关闭屏幕时获取视图控制器
通过container controllers(容器控制器)
通过presentation(呈现) 和 dismissal (解散)
通过直接视图操作
Containers(容器)
- (void)pushViewController: animated: - (void)popViewControllerAnimated:
- (void) presentModalViewController: animated: - (void) dismissModalViewControllerAnimated:
- (void)presentViewController: (UIViewController *)vc animated: (BOOL)animated completion: (void (^)(void))completion; - (void)dismissViewControllerAnimated:(BOOL)animated completion: (void (^)(void))completion;
- (UIViewController *)parentViewController; - (UIViewController *)presentingViewController;
[root.someView addSubview: vc.view]
[rootVC addChildViewController: vc]
你会在什么时候考虑创建一个自定义视图控制器容器? Aesthetics(考虑到美观)
自定义应用程序流 您的应用程序直接处理视图层次结构
@protocol UISplitViewControllerDelegate ... // Returns YES if a view controller should be hidden by // the split view controller in a given orientation. // (This method is only called on the leftmost view controller // and only discriminates portrait from landscape.) - (BOOL)splitViewController: (UISplitViewController*)svc shouldHideViewController:(UIViewController *)vc inOrientation:(UIInterfaceOrientation)orientation; @end
为修正一个食谱应用创建一个应用程序流
Container View Controller Demo
- (IBAction)flipToNote { if(...) { ... [self addChildViewController:_noteController]; [self transitionFromViewController:_contentController toViewController:_noteController duration:.5 options:UIViewAnimationOptionTransitionFlipFromRight animations:nil completion:^(BOOL finished) { _flipNoteButton.title = @"Hide Note"; _flipNoteButton.action = @selector(flipFromNote); [_noteController didMoveToParentViewController:self]; }]; } }
- (IBAction)flipFromNote { if(_isNoteBeingShown) { [_noteController willMoveToParentViewController:nil]; [self transitionFromViewController:_noteController toViewController:_contentController duration:0.5 options:UIViewAnimationOptionTransitionFlipFromLeft animations:nil completion:^(BOOL finished) { _flipNoteButton.title = @"Show Note"; _flipNoteButton.action = @selector(flipToNote); [_noteController removeFromParentViewController]; _isNoteBeingShown = NO; }]; } }
- (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; if(![self isMovingToParentViewController]) { [[self parentViewController] updateSelectionForListOfContentIdentifiersIfNecessary]; } }
- (BOOL)isMovingToParentViewController; // Used in appearance callbacks - (BOOL)isMovingFromParentViewController; // Used in disappearance callbacks - (BOOL)isBeingPresented; - (BOOL)isBeingDismissed;
- (BOOL)automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers;
- (IBAction)flipToNote { if(...) { ... [self addChildViewController:_noteController]; [_noteController viewWillAppear: YES]; // Some fancy animation that culminates in the view swap // E.g [[_contentController.view superview] addSubview:_noteController.view]; ... // Finally this is usually called in a completion handler // after the animation completes [_noteController viewDidAppear: YES]; [_noteController didMoveToParentViewController:self]; } }
为修正一个食谱应用创建一个应用程序流 演示亮点 — — 定义演示文稿上下文
- (IBAction)emailContent { UIViewController *presenter= _isNoteBeingShown ? _noteController: _contentController; ... mailController.modalPresentationStyle = UIModalPresentationCurrentContext; if(_contentController && [MFMailComposeViewController canSendMail]) { ... data = [_contentProvider dataForContentIdentifier:self.contentControllerIdentifier mimeType:&mimeType]; note = [_contentProvider noteForContentIdentifier:self.contentControllerIdentifier]; ... [presenter presentViewController:mailController animated:YES completion:^{[mailController release];}]; } }
mc.modalPresentationStyle = UIModalPresentationCurrentContext; [rb presentViewController:mailController animated:YES completion:^{...}];
mc.modalPresentationStyle = UIModalPresentationCurrentContext; [note presentViewController:mailController animated:YES completion:^{...}
- (void)viewDidLoad { [super viewDidLoad]; self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; self.definesPresentationContext = YES; ... }
@property(nonatomic,assign) BOOL definesPresentationContext; // A controller that defines the presentation context can also // specify the modal transition style if this property is true. @property(nonatomic,assign) BOOL providesPresentationContextTransitionStyle;
视图控制器可以管理多个视图 不是每个视图都需要一个视图控制器
定义新的应用程序或实现 而不是直接视图操作(这将创造出具有前瞻性的应用)
但理解你的层次结构
导航与视图之间的页面卷曲过渡
管理子视图控制器的当前内容
展示一个已准备的应用程序流程
- initWithTransitionStyle: navigationOrientation: options:
UIPageViewController *myPVC = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:[NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInteger:UIPageViewControllerSpineLocationMid], UIPageViewControllerOptionSpineLocationKey]]
- setViewControllers: direction: animated: completion:
[myPVC setViewControllers:[NSArray arrayWithObjects:firstVC, secondVC, nil] direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
[myPVC setViewControllers:[NSArray arrayWithObjects:thirdVC, fourthVC, nil] direction:UIPageViewControllerNavigationDirectionForward animated: NO completion:nil]
YES ^(BOOL finished) { NSLog(@"Page curl completed."); }];
[myPVC setViewControllers:[NSArray arrayWithObjects:thirdVC, fourthVC, nil] direction:UIPageViewControllerNavigationDirectionForward animated: completion: YES ^(BOOL finished) { NSLog(@"Page curl completed."); }];
初始化页面视图控制器与过渡样式,导航定位和任何选项 (spine location) 设置初始视图控制器 (和驱动编程导航) 通过设置数据源允许用户驱动导航
通过放置手势识别器自定义手势区 在旋转和拖拽过程中改变spine location
使用自定义视图控制器容器...。。。定义新的应用程序或looks 。。。代替直接视图操作
尽可能利用现有的容器UINavigationController, UITabBarController, UISplitViewController, etc. 新的容器视图控制器, UIPageViewController