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,用来显示起始位置,并设置了用于标注的图像。

  1. _startMapViewcontroller 被添加成 root view controller 的一个
    child。这会自动在 child
    上调用willMoveToParentViewController: 方法。

  2. child 的 view 被添加成 container view 的 subview。

  3. child 被通知到它现在有一个 parent view controller。

  4. 用来显示地理位置的 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
}];
}


  1. 在开始动画之前,我们把 toController 作为一个 child
    进行添加,并通知 fromController 它将被移除。如果fromController 的
    view 是容器 view 层级的一部分,它的 viewWillDisapear: 方法就会被调用。

  2. toController 被告知它有一个新的 parent,并且适当的 view 事件方法将被调用。

  3. 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容器,布布扣,bubuko.com

时间: 2024-11-03 21:46:43

View Controller容器的相关文章

View Controller 转场

自定义转场动画 iOS 7 中最让我激动的特性之一就是提供了新的 API 来支持自定义 view contrioller 之间的转场动画.iOS 7 发布之前,我自己写过一些 view controller 之间的转场动画,这是一个比较头疼的过程,而且这种做法并不被苹果完全地支持,尤其是如果你想让这个转场动画有交互式的效果就更难了. 在继续阅读之前,我需要先声明一下:这个 API 是新近才发布的,目前还没有所谓的最佳实践.通常来说,开发者需要探索几个月才能得出关于新 API 的最佳实践.因此请将

【IOS笔记】View Controller Basics

View Controller Basics   视图控制器基础 Apps running on iOS–based devices have a limited amount of screen space for displaying content and therefore must be creative in how they present information to the user. Apps that have lots of information to display

M2在奋斗之ios开发--View Controller pragramming guide for IOS中文版

About View Controllers 视图控制器是应用程序数据和其视觉外形之间的一个至关重要的链接.无论何时,应用程序显示一个用户界面,其显示的内容都是由一个或一组互相合作的视图控制器管理.因此,视图控制器给你建立的应用程序提供了骨架. iOS提供了很多内置的视图控制器类来支持标准用户界面块(piece),比如导航和标签栏.作为开发应用程序的一部分,你还可以实现一个或多个自定义控制器来显示应用程序的特定内容. 概述 在模型-视图-控制器(MVC)设计模式里,视图控制器是传统的控制器对象,

iOS架构师之路:控制器(View Controller)瘦身设计

前言 古老的MVC架构是容易被iOS开发者理解和接受的设计模式,但是由于iOS开发的项目功能越来越负责庞大,项目代码也随之不断壮大,MVC的模糊定义导致我们的业务开发工程师很容易把大量的代码写到视图控制器中,行业中对这种控制器有个专业词汇Massive ViewControler(臃肿的视图控制器).代码臃肿导致可读性可维护性差,而且这种不清晰的设计还有许多的副作用,比如代码重用性差.作为架构师需要关注项目的代码质量.指导业务开发工程师写出高质量,高健壮性,高可用的代码也是很重要的工作.因此需要

《iOS Human Interface Guidelines》——Container View Controller

容器视图控制器 容器视图控制器管理和展示它的子视图集合--或者子控制器集合--以一种自定义的方式.系统定义的容器视图控制器的例子有标签栏视图控制器.导航栏视图控制器和分栏视图控制器(查看Tab Bar.Navigation Bar和Split View Controller来学习更多关于这些元素的内容). API NOTE 查看UIViewController Class Reference学习关于在你的代码中定义自定义的容器视图控制器的内容. 容器视图控制器没有预定义的外观和行为. 使用容器视

自定义View Controller转换动画

原文链接 : Introduction to Custom View Controller Transitions and Animations 原文作者 : joyce echessa 译文出自 : 开发技术前线 www.devtf.cn 译者 : kmyhy 观察 iOS 自带的 App,你会看到当你从一个视图导航到另一个视图时总是会显示各种各样的转换动画,以"主-从"视图为例(类似的程序有Messages App或者系统设置程序),一个轻扫动作能够让详情视图呈现在主视图之上,在呈

iOS开发 - View Controller 的paging

在学习开源中国ios 源码时,发现其实现view controllers 的paging的方法如下: 第一.定义一个容器类的view controller,类似于UIPageViewController. 该vc的主要由两部分组成: a 一个title view,用来表明当前内容是什么,并且能够相应用户的点击,切换content b 一个table view controller (通过addChildViewController添加,并将table view controller 的view

Custom Container View Controller

什么是Container View Controller?苹果文档是这么描述的:  A container view controller contains content owned by other view controllers. 也就是说一个View Controller显示的某部分内容属于另一个View Controller,那么这个View Controller就是一个Container,比如UIKit中的UINavigationController,UITabBarControl

Swift:超炫的View Controller切换动画

匿名社交应用Secret的开发者开发了一款叫做Ping的应用,用户可以他们感兴趣的话题的推送. Ping有一个很炫的东西,就是主界面和之间切换的动画做的非常的好.每次看到一个非常炫的动画,都不由得会想:“这个东西我要不要自己实现以下”.哈哈~~~ 这个教程里,你会学到如何用Swift实现这样的很酷的动画.你会学到如何使用shape layer,遮罩和使用UIViewControllerAnimnatedTransitioning协议和UIPercentDrivenInteractivetrans