原文地址:http://www.cnblogs.com/CHONGCHONG2008/archive/2012/08/02/2619366.html
窗口和视图
窗口和视图是为iPhone应用程序构造用户界面的可视组件。窗口为内容显示提供背景平台,而视图负责绝大部分的内容描画,并负责响应用户的交互。虽然本章讨论的概念和窗口及视图都相关联,但是讨论过程更加关注视图,因为视图对系统更为重要。
视图对iPhone应用程序是如此的重要,以至于在一个章节中讨论视图的所有方面是不可能的。本章将关注窗口和视图的基本属性、各个属性之间的关系、以及在应用程序中如何创建和操作这些属性。本章不讨论视图如何响应触摸事件或如何描画定制内容,有关那些主题的更多信息,请分别参见“事件处理”和“图形和描画”部分。
什么是窗口和视图?
和Mac OS X一样,iPhone OS通过窗口和视图在屏幕上展现图形内容。虽然窗口和视图对象之间在两个平台上有很多相似性,但是具体到每个平台上,它们的作用都有轻微的差别。
UIWindow的作用
和Mac OS X的应用程序有所不同,iPhone应用程序通常只有一个窗口,表示为一个UIWindow类的实例。您的应用程序在启动时创建这个窗口(或者从nib文件进行装载),并往窗口中加入一或多个视图,然后将它显示出来。窗口显示出来之后,您很少需要再次引用它。
在iPhone OS中,窗口对象并没有像关闭框或标题栏这样的视觉装饰,用户不能直接对其进行关闭或其它操作。所有对窗口的操作都需要通过其编程接口来实现。应用程序可以借助窗口对象来进行事件传递。窗口对象会持续跟踪当前的第一响应者对象,并在UIApplication对象提出请求时将事件传递它。
还有一件可能让有经验的Mac OS X开发者觉得奇怪的事是UIWindow类的继承关系。在Mac OS X中,NSWindow的父类是NSResponder;而在iPhone OS中,UIWindow的父类是UIView。因此,窗口在iPhone OS中也是一个视图对象。不管其起源如何,您通常可以将iPhone OS上的窗口和Mac OS X的窗口同样对待。也就是说,您通常不必直接操作UIWindow对象中与视图有关的属性变量。
在创建应用程序窗口时,您应该总是将其初始的边框尺寸设置为整个屏幕的大小。如果您的窗口是从nib文件装载得到,Interface Builder并不允许创建比屏幕尺寸小的窗口;然而,如果您的窗口是通过编程方式创建的,则必须在创建时传入期望的边框矩形。除了屏幕矩形之外,没有理由传入其它边框矩形。屏幕矩形可以通过UIScreen对象来取得,具体代码如下所示:
UIWindow* aWindow = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
虽然iPhone OS支持将一个窗口叠放在其它窗口的上方,但是您的应用程序永远不应创建多个窗口。系统自身使用额外的窗口来显示系统状态条、重要的警告、以及位于应用程序窗口上方的其它消息。如果您希望在自己的内容上方显示警告,可以使用UIKit提供的警告视图,而不应创建额外的窗口。
UIView是作用视图
是UIView类的实例,负责在屏幕上定义一个矩形区域。在iPhone的应用程序中,视图在展示用户界面及响应用户界面交互方面发挥关键作用。每个视图对象都要负责渲染视图矩形区域中的内容,并响应该区域中发生的触碰事件。这一双重行为意味着视图是应用程序与用户交互的重要机制。在一个基于模型-视图-控制器的应用程序中,视图对象明显属于视图部分。
除了显示内容和处理事件之外,视图还可以用于管理一或多个子视图。子视图是指嵌入到另一视图对象边框内部的视图对象,而被嵌入的视图则被称为父视图或超视图。视图的这种布局方式被称为视图层次,一个视图可以包含任意数量的子视图,通过为子视图添加子视图的方式,视图可以实现任意深度的嵌套。视图在视图层次中的组织方式决定了在屏幕上显示的内容,原因是子视图总是被显示在其父视图的上方;这个组织方法还决定了视图如何响应事件和变化。每个父视图都负责管理其直接的子视图,即根据需要调整它们的位置和尺寸,以及响应它们没有处理的事件。
由于视图对象是应用程序和用户交互的主要途径,所以需要在很多方面发挥作用,下面是其中的一小部分:
描画和动画
视图负责对其所属的矩形区域进行描画。
某些视图属性变量可以以动画的形式过渡到新的值。
布局和子视图管理
视图管理着一个子视图列表。
视图定义了自身相对于其父视图的尺寸调整行为。
必要时,视图可以通过代码调整其子视图的尺寸和位置。
视图可以将其坐标系统下的点转换为其它视图或窗口坐标系统下的点。
事件处理
视图可以接收触摸事件。
视图是响应者链的参与者。
在iPhone应用程序中,视图和视图控制器紧密协作,管理若干方面的视图行为。视图控制器的作用是处理视图的装载与卸载、处理由于设备旋转导致的界面旋转,以及和用于构建复杂用户界面的高级导航对象进行交互。更多这方面的信息请参见“视图控制器的作用”部分。
本章的大部分内容都着眼于解释视图的这些作用,以及说明如何将您自己的定制代码关联到现有的UIView行为中。
UIKit的视图类
UIView类定义了视图的基本行为,但并不定义其视觉表示。相反,UIKit通过其子类来为像文本框、按键、及工具条这样的标准界面元素定义具体的外观和行为。图2-1显示了所有UIKit视图类的层次框图。除了UIView和UIControl类是例外,这个框图中的大多数视图都设计为可直接使用,或者和委托对象结合使用。
图2-1 视图的类层次
View class hierarchy
这个视图层次可以分为如下几个大类:
容器
容器视图用于增强其它视图的功能,或者为视图内容提供额外的视觉分隔。比如,UIScrollView类可以用于显示因内容太大而无法显示在一个屏幕上的视图。UITableView类是UIScrollView类的子类,用于管理数据列表。表格的行可以支持选择,所以通常也用于层次数据的导航—比如用于挖掘一组有层次结构的对象。
UIToolbar对象则是一个特殊类型的容器,用于为一或多个类似于按键的项提供视觉分组。工具条通常出现在屏幕的底部。Safari、Mail、和Photos程序都使用工具条来显示一些按键,这些按键代表经常使用的命令。工具条可以一直显示,也可以根据应用程序的需要进行显示。
控件
控件用于创建大多数应用程序的用户界面。控件是一种特殊类型的视图,继承自UIControl超类,通常用于显示一个具体的值,并处理修改这个值所需要的所有用户交互。控件通常使用标准的系统范式(比如目标-动作模式和委托模式)来通知应用程序发生了用户交互。控件包括按键、文本框、滑块、和切换开关。
显示视图
控件和很多其它类型的视图都提供了交互行为,而另外一些视图则只是用于简单地显示信息。具有这种行为的UIKit类包括UIImageView、 UILabel、UIProgressView、UIActivityIndicatorView。
文本和web视图
文本和web视图为应用程序提供更为高级的显示多行文本的方法。UITextView类支持在滚动区域内显示和编辑多行文本;而UIWebView类则提供了显示HTML内容的方法,通过这个类,您可以将图形和高级的文本格式选项集成到应用程序中,并以定制的方式对内容进行布局。
警告视图和动作表单
警告视图和动作表单用于即刻取得用户的注意。它们向用户显示一条消息,同时还有一或多个可选的按键,用户通过这些按键来响应消息。警告视图和动作表单的功能类似,但是外观和行为不同。举例来说,UIAlertView类在屏幕上弹出一个蓝色的警告框,而UIActionSheet类则从屏幕的底部滑出动作框。
导航视图
页签条和导航条和视图控制器结合使用,为用户提供从一个屏幕到另一个屏幕的导航工具。在使用时,您通常不必直接创建UITabBar和UINavigationBar的项,而是通过恰当的控制器接口或Interface Builder来对其进行配置。
窗口
窗口提供一个描画内容的表面,是所有其它视图的根容器。每个应用程序通常都只有一个窗口。更多信息请参见“UIWindow的作用”部分。
除了视图之外,UIKit还提供了视图控制器,用于管理这些对象。更多信息请参见“视图控制器的作用”部分。
视图控制器的作用
运行在iPhone OS上的应用程序在如何组织内容和如何将内容呈现给用户方面有很多选择。含有很多内容的应用程序可以将内容分为多个屏幕。在运行时,每个屏幕的背后都是一组视图对象,负责显示该屏幕的数据。一个屏幕的视图后面是一个视图控制器其作用是管理那些视图上显示的数据,并协调它们和应用程序其它部分的关系。
UIViewController类负责创建其管理的视图及在低内存时将它们从内容中移出。视图控制器还为某些标准的系统行为提供自动响应。比如,在响应设备方向变化时,如果应用程序支持该方向,视图控制器可以对其管理的视图进行尺寸调整,使其适应新的方向。您也可以通过视图控制器来将新的视图以模式框的方式显示在当前视图的上方。
除了基础的UIViewController类之外,UIKit还包含很多高级子类,用于处理平台共有的某些高级接口。特别需要提到的是,导航控制器用于显示多屏具有一定层次结构的内容;而页签条控制器则支持用户在一组不同的屏幕之间切换,每个屏幕都代表应用程序的一种不同的操作模式。
有关如何通过视图控制器管理用户界面上视图的更多信息,请参见iPhone OS的视图控制器编程指南。
视图架构和几何属性
由于视图是iPhone应用程序的焦点对象,所以对视图与系统其它部分的交互机制有所了解是很重要的。UIKit中的标准视图类为应用程序免费提供相当数量的行为,还提供了一些定义良好的集成点,您可以通过这些集成点来对标准行为进行定制,完成应用程序需要做的工作。
本文的下面部分将解释视图的标准行为,并说明哪些地方可以集成您的定制代码。如果需要特定类的集成点信息,请参见该类的参考文档。您可以从UIKit框架参考中取得所有类参考文档的列表。
视图交互模型
任何时候,当用户和您的程序界面进行交互、或者您的代码以编程的方式进行某些修改时,UIKit内部都会发生一个复杂的事件序列。在事件序列的一些特定的点上,UIKit会调用您的视图类,使它们有机会代表应用程序进行事件响应。理解这些调用点是很重要的,有助于理解您的视图对象和系统在哪里进行结合。图2-2显示了从用户触击屏幕到图形系统更新屏幕内容这一过程的基本事件序列。以编程方式触发事件的基本步骤与此相同,只是没有最初的用户交互。
图2-2 UIKit和您的视图对象之间的交互
UIKit interactions with your view objects
下面的步骤说明进一步刨析了图2-2中的事件序列,解释了序列的每个阶段都发生了什么,以及应用程序可能如何进行响应。
用户触击屏幕。
硬件将触击事件报告给UIKit框架。
UIKit框架将触击信息封装为一个UIEvent对象,并派发给恰当的视图(有关UIKit如何将事件递送给您的视图的详细解释,请参见“事件的传递”部分)。
视图的事件处理方法可以通过下面的方式来响应事件:
调整视图或其子视图的属性变量(边框、边界、透明度等)。
将视图(或其子视图)标识为需要修改布局。
将视图(或其子视图)标识为布局需要重画。
将数据发生的变化通报给控制器。
当然,上述的哪些事情需要做及调用什么方法来完成是由视图来决定的。
如果视图被标识为需要重新布局,UIKit就调用视图的layoutSubviews方法。
您可以在自己的定制视图中重载这个方法,以便调整子视图的尺寸和位置。举例来说,如果一个视图具有很大的滚动区域,就需要使用几个子视图来“平铺”,而不是创建一个内存很可能装不下的大视图。在这个方法的实现中,视图可以隐藏所有不需显示在屏幕上的子视图,或者在重新定位之后将它们用于显示新的内容。作为这个过程的一部分,视图也可以将用于“平铺”的子视图标识为需要重画。
如果视图的任何部分被标识为需要重画,UIKit就调用该视图的drawRect:方法。
UIKit只对那些需要重画的视图调用这个方法。在这个方法的实现中,所有视图都应该尽可能快地重画指定的区域,且都应该只重画自己的内容,不应该描画子视图的内容。在这个调用点上,视图不应该尝试进一步改变其属性或布局。
所有更新过的视图都和其它可视内容进行合成,然后发送给图形硬件进行显示。
图形硬件将渲染完成的内容转移到屏幕。
请注意:上述的更新模型主要适用于采纳内置视图和描画技术的应用程序。如果您的应用程序使用OpenGL ES来描画内容,则通常要配置一个全屏的视图,然后直接在OpenGL的图形上下文中进行描画。您的视图仍然需要处理触碰事件,但不需要对子视图进行布局或者实现drawRect:方法。有关OpenGL ES的更多信息,请参见“用OpenGL ES进行描画”部分。
基于上述的步骤说明可以看出,UIKit为您自己定制的视图提供如下主要的结合点:
下面这些事件处理方法:
touchesBegan:withEvent:
touchesMoved:withEvent:
touchesEnded:withEvent:
touchesCancelled:withEvent:
layoutSubviews方法
drawRect:方法
大多数定制视图通过实现这些方法来得到自己期望的行为。您可能不需要重载所有方法,举例来说,如果您实现的视图是固定尺寸的,则可能不需要重载layoutSubviews方法。类似地,如果您实现的视图只是显示简单的内容,比如文本或图像,则通常可以通过简单地嵌入UIImageView和UILabel对象作为子视图来避免描画。
重要的是要记住,这些是主要的结合点,但不是全部。UIView类中有几个方法的设计目的就是让子类重载的。您可以通过查阅UIView类参考中的描述来了解哪些方法可以被重载。
视图渲染架构
虽然您通过视图来表示屏幕上的内容,但是UIView类自身的很多基础行为却严重依赖于另一个对象。UIKit中每个视图对象的背后都有一个Core Animation层对象,它是一个CALayer类的实例,该类为视图内容的布局和渲染、以及合成和动画提供基础性的支持。
和Mac OS X(在这个平台上Core Animation支持是可选的)不同的是,iPhone OS将Core Animation集成到视图渲染实现的核心。虽然Core Animation发挥核心作用,但是UIKit在Core Animation上面提供一个透明的接口层,使编程体验更为流畅。这个透明的接口使开发者在大多数情况下不必直接访问Core Animation的层,而是通过UIView的方法和属性声明取得类似的行为。然而,当UIView类没有提供您需要的接口时,Core Animation就变得重要了,在那种情况下,您可以深入到Core Animation层,在应用程序中实现一些复杂的渲染。