移动开发:初学 iOS-UIViewController 心得

初学 iOS,本文翻译了一些 iOS 官网上的 UIViewController 的知识点,如有不到位或不正确的地方,还请指正:

本文所介绍的内容的目标:

  • 理解content view controllers 和 container view controllers

  • 知道如何实现自定义view controller containers 以及 何时使用view controller containers

  • 在iOS5操作系统下使用UIPageViewController

  • 分享一些新的API和使用技巧

这是一个UIViewController

这是一个UIViewController

我们为什么要使用视图控制器(View Controllers)?

两个原因

  1. 使用它能够做出更高品质的APP
  2. 它们可被继承

视图控制器(基础知识)

设计模式


设计模式
  • 它们是MVC中的C

  • 它们管理着“整个屏幕里显示的内容”

  • 它们通常和它们的对象模型打包在一起

使用系统视图控制器(System View Controllers)

这里写图片描述

系统视图控制器通包括以下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
根视图2

自定义视图控制器

  • 子类的UIViewController
  • 视图控制器与视图层关联
  • 重写所选择的API
  • 在应用程序中添加逻辑
  • 配置tango执行应用程序

Appearance callbacks

(void)viewWillAppear:
(void)viewDidAppear:
(void)viewWillDisappear:
(void)viewDidDisappear:

Rotation callbacks

 (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应用程序流

路线图

了解视图控制器容器

  • 视图控制器和视图层次

  • 视图控制器互相连接的三种方式

  • 设计自定义容器控制器

  • 探讨如何新增和修改API

视图控制器

两个层次的故事


两个层次的故事

蓝色:视图颜色
蓝色箭头:子视图关系

金色: 视图控制器颜色
灰色箭头:父视图控制器关系

相对于内容的容器

层次关系

容器层次关系原图

容器层次关系翻译

视图控制器容器

层次关系

控制器层次关系原图1
控制器层次关系翻译1
控制器层次关系原图2
控制器层次关系翻译2
控制器层次关系
控制器层次关系分解

视图控制器容器

层次关系

  • 你应该知道些什么?
    容器控制器是负责 父/子 关系的

视图控制器容器

API和控制器层次

邮件视图
控制器层次结构
控制器层次结构翻译

API和控制器层次

视图2
控制器层次结构2

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
    食谱控制器

控制器层次结构3

显示或者旋转的回调的触发的源头来自于window,一个app首先有一个主window,初始化的时候需要给这个主window指定一个rootViewController,window会将显示相关的回调(viewWillAppear:, viewWillDisappear:, viewDidAppear:, or viewDidDisappear: )以及旋转相关的回调(willRotateToInterfaceOrientation:duration: ,willAnimateRotationToInterfaceOrientation:duration:, didRotateFromInterfaceOrientation:)传递给rootViewController。rootViewController需要再将这些callbacks的调用传递给它的Child View Controllers。

视图控制器容器

API和控制器层次

  • 添加和删除子控制器
(void)addChildViewController:(UIViewController *)childController; (void)removeFromParentViewController;
  • 访问子控制器
@property(nonatomic,readonly) NSArray *childViewControllers;
  • 回调
(void)willMoveToParentViewController:(UIViewController *)parent; (void)didMoveToParentViewController:(UIViewController *)parent;

视图控制器容器

层次关系

  • 你应该知道些什么?
    容器控制器是负责 父/子 关系的
    有一致和不一致的层次结构

视图控制器容器

不一致的层次结构

不一致的层次结构

UIViewControllerHierarchyInconsistencyException
为什么会这么糟糕?

exception

视图控制器容器

层次关系

  • 你应该知道些什么?
    容器控制器是负责 父/子 关系的
    有一致和不一致的层次结构
    这时,其实出现了回调

callbacks
[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](if necessary)之前调用

  • viewDidAppear:
    该视图添加到视图层次结构之后调用
    在 [vc.view layoutSubviews](if necessary)之后调用

  • viewWillDisappear:
    该视图从 windows 的视图层次结构移除之前调用

  • viewDidDisappear:
    该视图从 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];
}];

视图控制器容器

API和控制器层次

  • 子视图控制器之间的转换
- (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(容器)

containers

- (void)pushViewController: 
animated:
- (void)popViewControllerAnimated:

Presentation and dismissal

presentation
- (void) presentModalViewController: 
animated: 
- (void) dismissModalViewControllerAnimated:

dimissal
- (void)presentViewController: (UIViewController *)vc 
animated: (BOOL)animated
completion: (void (^)(void))completion;
- (void)dismissViewControllerAnimated:(BOOL)animated
completion: (void (^)(void))completion;

presentation2
- (UIViewController *)parentViewController;
- (UIViewController *)presentingViewController;

presentation3
presentation4

视图操作

presentation5

[root.someView addSubview: vc.view]

manipulation1

[rootVC addChildViewController: vc]

manipulation2

视图控制器容器

inception(启动)

  • 你会在什么时候考虑创建一个自定义视图控制器容器?
    Aesthetics(考虑到美观)
    自定义应用程序流
    您的应用程序直接处理视图层次结构
inception

inception(启动)仅在iPad上

@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

设计一个新的应用程序流

为修正一个食谱应用创建一个应用程序流

recipe1
recipe2
recipe3

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;
}];
}
}

recipe4
recipe5

Moving in and out of containers

- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if(![self isMovingToParentViewController]) {
[[self parentViewController] updateSelectionForListOfContentIdentifiersIfNecessary];
}
}

inception(启动)

- (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];}];
}
}

recipe6
mc.modalPresentationStyle = UIModalPresentationCurrentContext;
[rb presentViewController:mailController 
animated:YES 
completion:^{...}];

recipe7
recipe8
mc.modalPresentationStyle = UIModalPresentationCurrentContext;
[note presentViewController:mailController 
animated:YES 
completion:^{...}
recipe9
- (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;

视图控制器容器

总结

  • 尽可能利用现有的容器

    视图控制器可以管理多个视图
    不是每个视图都需要一个视图控制器

  • 当需要时,创建自定义视图控制器容器

    定义新的应用程序或实现
    而不是直接视图操作(这将创造出具有前瞻性的应用)

  • 该 API 很简单

    但理解你的层次结构

UIPageViewController

导航与视图之间的页面卷曲过渡

curl

UIPageViewController

一个容器视图控制器

  • 管理子视图控制器的当前内容

  • 展示一个已准备的应用程序流程

Initialization(初始化)

initialization
- initWithTransitionStyle:
navigationOrientation:
options:
UIPageViewController *myPVC = [[UIPageViewController alloc]
initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl
navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
options:[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInteger:UIPageViewControllerSpineLocationMid],
UIPageViewControllerOptionSpineLocationKey]]

初始视图控制器

Initial
- 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.");
}];

交互-用户驱动导航

driven

Demo 总结

  • 接下来我们知道

    初始化页面视图控制器与过渡样式,导航定位和任何选项 (spine location)
    设置初始视图控制器 (和驱动编程导航)
    通过设置数据源允许用户驱动导航
    
  • 我们从中学到什么:

    通过放置手势识别器自定义手势区
    在旋转和拖拽过程中改变spine location 
    
  • 理解内容和容器视图控制器之间的区别

  • 使用自定义视图控制器容器...
    。。。定义新的应用程序或looks
    。。。代替直接视图操作

  • 尽可能利用现有的容器
    UINavigationController, UITabBarController, UISplitViewController, etc.
    新的容器视图控制器, UIPageViewController

OneAPM Mobile Insight ,监控网络请求及网络错误,提升用户留存。访问 OneAPM 官方网站感受更多应用性能优化体验,想阅读更多技术文章,请访问 OneAPM 官方技术博客
本文转自 OneAPM 官方博客

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容