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

1,799 阅读9分钟
原文链接: news.oneapm.com

初学 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之前调用

  • viewDidAppear: 该视图添加到视图层次结构之后调用 在 vc.view layoutSubviews之后调用

  • 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 官方技术博客