Views

Because view objects are the main way your application interacts with the user, they have many responsibilities. Here are just a few:

因为视图对象是应用程序跟用户交互的主要方式,所以它们有很多责任。以下是其中一小部分:

  • Layout and subview management

    布局和子视图管理

    • A view defines its own default resizing behaviors in relation to its parent view.

      一个视图根据它的父视图来定义它自己的默认调整尺寸行为。

    • A view can manage a list of subviews.

      一个视图能管理一系列子视图。

    • A view can override the size and position of its subviews as needed.

      一个视图能根据需要覆盖(override)它子视图的尺寸和位置。

    • A view can convert points in its coordinate system to the coordinate systems of other views or the window.

      一个视图能把位于自己坐标系里的点转换为别的视图或窗口的坐标系点。

  • Drawing and animation

    绘图和动画

    • A view draws content in its rectangular area.

      一个视图在它自己的矩形区域内绘制内容。

    • Some view properties can be animated to new values.

      一些视图属性能通过动画设置新值。

  • Event handling

    事件处理

    • A view can receive touch events.

      一个视图能接收触摸事件。

    • A view participates in the responder chain.

      一个视图是响应器链中的一员。

This chapter focuses on the steps for creating, managing, and drawing views and for handling the layout and management of view hierarchies. For information about how to handle touch events (and other events) in your views, see Event Handling Guide for iOS.

本章重点描述创建,管理,绘制视图的步骤,处理布局并管理视图层次结构的步骤。 关于如何处理视图的触摸事件(以及其它事件)的信息,请看Event Handling Guide for iOS.

Creating and Configuring View Objects

一、创建和配置视图对象

You create views as self-contained objects either programmatically or using Interface Builder, and then you assemble them into view hierarchies for use.

你可以通过程序或使用界面生成器(Interface Builder)来创建视图,把它们作为自包含对象,然后把它们加入视图层次里以便使用。

Creating View Objects Using Interface Builder

1、用界面生成器创建视图对象

The simplest way to create views is to assemble them graphically using Interface Builder. From Interface Builder, you can add views to your interface, arrange those views into hierarchies, configure each view’s settings, and connect view-related behaviors to your code. Because Interface Builder uses live view objects—that is, actual instances of the view classes—what you see at design time is what you get at runtime. You then save those live objects in a nib file, which is a resource file that preserves the state and configuration of your objects.

创建视图最简单的方法是使用界面生成器。 在界面生成器里,你可以把视图添加到你的界面,并把它们组织到层次结构里,配置每个视图的设置(settings), 以及把视图相关的行为连接到代码。 因为界面生成器使用活得视图对象---就是视图类的真实实例--所以设计时你看到的跟运行时你看到的一样。然后你把这些活的对象保存在一个nib文件,nib文件是一个资源文件,它保留你所有对象的状态和配置。

You usually create nib files in order to store an entire view hierarchy for one of your application’s view controllers. The top level of the nib file usually contains a single view object that represents your view controller’s view. (The view controller itself is typically represented by the File’s Owner object.) The top-level view should be sized appropriately for the target device and contain all of the other views that are to be presented. It is rare to use a nib file to store only a portion of your view controller’s view hierarchy.

通常,你创建nib文件是为了存储一个完整的视图层次,该视图层次属于应用程序的其中一个视图控制器。nib文件的顶层通常包含一单个视图对象,它表示视图控制器的视图。(视图控制器本身通常由File's Owner对象来表示) 顶层视图应该根据目标设备调整为合适的尺寸, 它包含了其它需要被呈现的所有的视图。一个nib文件只用来存储视图控制器中视图层次的一部分的情况很少。

When using nib files with a view controller, all you have to do is initialize the view controller with the nib file information. The view controller handles the loading and unloading of your views at the appropriate times. However, if your nib file is not associated with a view controller, you can load the nib file contents manually using an NSBundleor UINib object, which use the data in the nib file to reconstitute your view objects.

 当nib文件跟一个视图控制器一起使用时,你只需要用nib文件的信息初始化视图控制器。视图控制器在合适的时候处理视图的载入和卸载。然而,如果你的nib文件没有跟一个视图控制器相关联,你可以通过NSBundle 或 UINib 对象(这些对象使用nib文件里的数据来重建视图对象)手动加载nib文件内容。

For more information about how to use Interface Builder to create and configure your views, see Interface Builder User Guide. For information about how view controllers load and manage their associated nib files, see “Custom View Controllers” in View Controller Programming Guide for iOS. For more information about how to load views programmatically from a nib file, see “Nib Files” in Resource Programming Guide.

关于使用界面生成器来创建和配置视图的更多信息,请看Interface Builder User Guide(界面生成器使用指南). 关于视图控制器如何加载和管理它们相关的nib文件的信息,请看View Controller Programming Guide for iOS 里的 “Custom View Controllers” 。 关于如何用程序从一个nib文件加载视图的更多信息,请看Resource Programming Guide 里的“Nib Files”

Creating View Objects Programmatically

2、用程序创建视图对象

If you prefer to create views programmatically, you can do so using the standard allocation/initialization pattern. The default initialization method for views is the initWithFrame: method, which sets the initial size and position of the view relative to its (soon-to-be-established) parent view. For example, to create a new generic UIView object, you could use code similar to the following:

如果你更喜欢通过程序创建视图,你可以使用标准的分配/初始化模式来完成。默认的视图初始化是initWithFrame: 方法,该方法根据它的父视图来设置视图的初始化尺寸和位置。比如,想要创建一个新的普通UIView对象,你应该使用类似以下代码:

CGRect  viewRect = CGRectMake(0, 0, 100, 100);
UIView* myView = [[UIView alloc] initWithFrame:viewRect];

Note: Although all views support the initWithFrame: method, some may have a preferred initialization methodthat you should use instead. For information about any custom initialization methods, see the reference documentation for the class.

 注意:尽管所有视图都支持initWithFrame: 方法,但是一些时候你可能应该需要用更好的初始化方法来替换它。 关于任何自定义初始化方法的信息,请看类的参考文档。

After you create a view, you must add it to a window (or to another view in a window) before it can become visible. For information on how to add views to your view hierarchy, see “Adding and Removing Subviews.”

当你创建完一个视图之后,你必须把它添加到一个窗口(或在窗口里的另一个视图)下,这样新视图才能被显示。 关于如何在视图层次结构里添加视图的信息,请看“Adding and Removing Subviews.”

Setting the Properties of a View

3、设置视图的属性

The UIView class has several declared properties for controlling the appearance and behavior of the view. These properties are for manipulating the size and position of the view, the view’s transparency, its background color, and its rendering behavior. All of these properties have appropriate default values that you can change later as needed. You can also configure many of these properties from Interface Builder using the Inspector window.

UIView 类有很多已经被声明的属性,用来控制视图的外形和行为。这些属性用来操作视图的尺寸和位置,视图的透明度,它的背景颜色以及它的渲染行为。所有这些属性都有合适的默认值,根据需要这些值可以被改变。 你也可以通过界面生成器的 Inspector window 来配置很多属性。

Table 3-1 lists some of the more commonly used properties (and some methods) and describes their usage. Related properties are listed together so that you can see the options you have for affecting certain aspects of the view.

表 3-1 列出了一些更常用的属性(以及一些方法),并描述了它们的用法。相关的属性都被列在一起,这样你就可以查看影响视图特定方面的各种选项。

Table 3-1  Usage of some key view properties
表 3-1 一个主要视图属性的用途
 Properties(属性)  Usage(用途)
 alphahiddenopaque  

These properties affect the opacity of the view. The alpha and hidden properties change the view’s opacity directly.

这些属性影响视图的透明度。alpha 和 hidden 属性直接改变视图的透明度属性。

The opaque property tells the system how it should composite your view. Set this property to YES if your view’s content is fully opaque and therefore does not reveal any of the underlying view’s content. Setting this property to YES improves performance by eliminating unnecessary compositing operations.

opaque属性告诉系统它应该如何合成视图。 把该视图设置为YES,视图的内容完全不透明,不显示任何底层视图内容。把该属性设置为YES能增强性能,因为它能消除不必要的合成操作。

 boundsframecenter,transform  

These properties affect the size and position of the view. The center and frameproperties represent the position of the view relative to its parent view. The framealso includes the size of the view. The bounds property defines the view’s visible content area in its own coordinate system.

这些属性影响视图的尺寸和位置。 center 和 frame属性表示视图相对于它父视图的位置。 frame属性还包含了视图的尺寸。 bounds 属性定义了视图在它自己的坐标系中的可视化内容区。

The transform property is used to animate or move the entire view in complex ways. For example, you would use a transform to rotate or scale the view. If the current transform is not the identity transform, the frame property is undefined and should be ignored.

transform 属性用来以复杂的方式动画或移动整个视图。比如,你可能使用一个变换来旋转或缩放视图。如果当前的变换不是恒等变换,frame属性未定义,应该被忽视。

For information about the relationship between the boundsframe, and centerproperties, see “The Relationship of the Frame, Bounds, and Center Properties.” For information about how transforms affect a view, see “Coordinate System Transformations.”

关于bounds, frame, 和center属性之间的关系的信息,请看“The Relationship of the Frame, Bounds, and Center Properties.” 关于变换如何影响视图的信息,请看“Coordinate System Transformations.”

 autoresizingMask,autoresizesSubviews

 These properties affect the automatic resizing behavior of the view and its subviews. The autoresizingMask property controls how a view responds to changes in its parent view’s bounds. The autoresizesSubviews property controls whether the current view’s subviews are resized at all.

这些属性影响视图及其子视图的自动调整尺寸行为。autoresizingMask 属性控制视图如何响应它父视图边界的改变。autoresizesSubviews属性控制当前视图的子视图是否已经全部被重新调整尺寸。

 contentMode,contentStretch,contentScaleFactor  

These properties affect the rendering behavior of content inside the view. ThecontentMode and contentStretch properties determine how the content is treated when the view’s width or height changes. The contentScaleFactorproperty is used only when you need to customize the drawing behavior of your view for high-resolution screens.

这些属性影响视图内部内容的渲染行为。当视图的宽度和高度改变时,contentMode 和 contentStretch 属性决定内容该被如何处理。contentScaleFactor 属性只在你需要给高分辨率屏幕上视图自定义绘制行为的时候才用。

For more information on how the content mode affects your view, see “Content Modes.” For information about how the content stretch rectangle affects your view, see “Stretchable Views.” For information about how to handle scale factors, see“Supporting High-Resolution Screens” in Drawing and Printing Guide for iOS.

关于内容模式如何影响你的视图的更多信息,请看 “Content Modes.” 关于内容扩展矩形如何影响你的视图的信息,请看“Stretchable Views.” 关于如何处理缩放因子的信息,请看Drawing and Printing Guide for iOS 的 “Supporting High-Resolution Screens”

 gestureRecognizers,userInteractionEnabled,

multipleTouchEnabled,exclusiveTouch
 

These properties affect how your view processes touch events. ThegestureRecognizers property contains gesture recognizers attached to the view. The other properties control what touch events the view supports.

这些属性影响你的视图如何处理触摸事件。 gestureRecognizers 属性包含连接到视图的手势识别。其它属性控制视图支持的触摸事件。

For information about how to respond to events in your views, see Event Handling Guide for iOS.

关于如何响应视图中的事件的信息,请看Event Handling Guide for iOS.

 backgroundColor,subviewsdrawRect:method, layer, (layerClass method)  

These properties and methods help you manage the actual content of your view. For simple views, you can set a background color and add one or more subviews. Thesubviews property itself contains a read-only list of subviews, but there are several methods for adding and rearranging subviews. For views with custom drawing behavior, you must override the drawRect: method.

这些属性和方法帮助你管理视图中的真实内容。对于简单视图,你可以设置一个背景颜色以及添加一个或多个子视图。 虽然subviews属性只包含了一个只读的子视图列表,但是有很多方法可以用来添加或重新整理子视图。对于带有自定义绘制行为的视图,你必须重载drawRect: 方法。

For more advanced content, you can work directly with the view’s Core Animationlayer. To specify an entirely different type of layer for the view (such as a layer that supports OpenGL ES drawing calls), you must override the layerClass method.

对于更先进的内容,你可以直接使用视图的内核动画层来工作。 想要给视图指定一整个不同类型的层(比如支持OpenGL ES 绘图调用的层),你必须重载layer 类方法。

For information about the basic properties common to all views, see UIView Class Reference. For more information about specific properties of a view, see the reference documentation for that view.

关于所有视图的通用基本属性的信息,请看 UIView Class Reference. 关于视图的特定属性的更多信息,请看那个视图的参考文档。

Tagging Views for Future Identification

4、标记视图以便将来能够识别它

The UIView class contains a tag property that you can use to tag individual view objects with an integer value. You can use tags to uniquely identify views inside your view hierarchy and to perform searches for those views at runtime. (Tag-based searches are faster than iterating the view hierarchy yourself.) The default value for the tag property is 0.

UIView 类包含了一个tag 属性,你可以用一个整数值来标记独特的视图对象。您可以使用标记来唯一地标识在您的视图层次的视图,并可以在运行时执行对那些视图的搜索。(基于标记的搜索比遍历视图层次本身快) tag属性的默认值是0。

To search for a tagged view, use the viewWithTag: method of UIView. This method performs a depth-first search of the receiver and its subviews. It does not search superviews or other parts of the view hierarchy. Thus, calling this method from the root view of a hierarchy searches all views in the hierarchy but calling it from a specific subview searches only a subset of views.

使用UIView类的viewWithTag: 方法来搜索一个标记的视图。 此方法执行接收器和它的子视图的深度优先搜索。它不搜索父视图或视图层次中的其它部分。因此,从一个视图层次根视图上调用该方法就可以搜索该层次上的所有视图,但是在一个特定的子视图上调用该视图就只搜索视图的一个子集。

Creating and Managing a View Hierarchy

二、创建和管理一个视图层次

Managing view hierarchies is a crucial part of developing your application’s user interface. The organization of your views influences both the visual appearance of your application and how your application responds to changes and events. For example, the parent-child relationships in the view hierarchy determine which objects might handle a specific touch event. Similarly, parent-child relationships define how each view responds to interface orientation changes.

管理视图层次是管理应用程序用户界面的一个关键部分。 视图组织同时影响应用程序的可见外形以及应用程序如何响应各种改变和事件。比如,视图层次中的父子关系决定哪个对象可能处理一个特定的触摸事件。 同样的,父子关系决定每个视图如何响应界面方向的各种改变。

Figure 3-1 shows an example of how the layering of views creates the desired visual effect for an application. In the case of the Clock application, the view hierarchy is composed of a mixture of views derived from different sources. The tab bar and navigation views are special view hierarchies provided by the tab bar and navigation controller objects to manage portions of the overall user interface. Everything between those bars belongs to the custom view hierarchy that the Clock application provides.

图 3-1 显示了视图的分层如何创建应用程序所需的视图效果。在时钟应用中,视图层次结构是由来自不同资源的各种视图的混合物组成的。标签栏和导航视图是特殊的视图层次结构, 它们由标签栏和导航控制器对象提供,用来管理整个用户界面的一部分。所有在那些栏中间的内容属于自定义视图层次结构,自定义层次结构由时钟应用程序提供。

Figure 3-1  Layered views in the Clock application

图 3-1 时钟应用程序中的视图层次

Layered views in the Clock application

There are several ways to build view hierarchies in iOS applications, including graphically in Interface Builder and programmatically in your code. The following sections show you how to assemble your view hierarchies and, having done that, how to find views in the hierarchy and convert between different view coordinate systems.

在iOS应用程序里有好几种方式可以建立视图层次结构,包括在界面生成器里图形化设计和在代码里用程序实现。接下来的章节显示你可以如何合成视图层次结构,以及当你完成层次后如何在层次结构里查找这些视图,以及如何在不同坐标系统里互相转换。

Adding and Removing Subviews

1、添加和删除子视图

Interface Builder is the most convenient way to build view hierarchies because you assemble your views graphically, see the relationships between the views, and see exactly how those views will appear at runtime. When using Interface Builder, you save your resulting view hierarchy in a nib file, which you load at runtime as the corresponding views are needed.

界面生成器是建立视图层次结构最方便的方式,因为你可以图形化合成你的视图,可以查看视图之间的关系,以及查看那些视图究竟是如何在运行时出现。当你使用界面生成器时,你在一个nib文件里存储你的结果视图层次结构。运行时,当需要相关的视图时,nib文件就会被载入。

If you prefer to create your views programmatically instead, you create and initialize them and then use the following methods to arrange them into hierarchies:

如果你更喜欢用程序来实现你的视图,你可以在创建并初始化它们之后使用以下方法来把它们组合到层次结构里:

  • To add a subview to a parent, call the addSubview: method of the parent view. This method adds the subview to the end of the parent’s list of subviews.

    要想给父视图添加一个子视图,调用父视图的addSubview: 方法。 该方法把子视图添加到父视图的子视图列表尾端。

  • To insert a subview in the middle of the parent’s list of subviews, call any of the insertSubview:... methods of the parent view. Inserting a subview in the middle of the list visually places that view behind any views that come later in the list.

    要想向父视图的子视图列表中间插入一个子视图,调用父视图的任何insertSubview:...方法。 向列表中间插入一个子视图,直观地把视图放在任何稍候就将出现的视图的后面。

  • To reorder existing subviews inside their parent, call the bringSubviewToFront:sendSubviewToBack:, orexchangeSubviewAtIndex:withSubviewAtIndex: methods of the parent view. Using these methods is faster than removing the subviews and reinserting them.

    要想重新排序父视图中的子视图,调用父视图的 bringSubviewToFront:sendSubviewToBack:,  或 exchangeSubviewAtIndex:withSubviewAtIndex: 方法。使用这些方法比删除子视图然后重新插入它们快。

  • To remove a subview from its parent, call the removeFromSuperview method of the subview (not the parent view).

    想从父视图删除一个子视图,调用子视图(不是父视图)的removeFromSuperview 方法。

When adding a subview to its parent, the subview’s current frame rectangle denotes its initial position inside the parent view. A subview whose frame lies outside of its superview’s visible bounds is not clipped by default. If you want your subview to be clipped to the superview’s bounds, you must explicitly set the clipsToBounds property of the superview to YES.

当你给父视图添加一个子视图时,子视图的当前框架矩形表示它在父视图里的初始位置。如何子视图的框架超出了其父视图的可见边界,该视图默认情况下不被裁剪。 如果你想让子视图根据父视图的边界被裁剪,你必须明确地把父视图的clipsToBounds 属性设置为YES.

The most common example of adding a subview to another view occurs in theapplication:didFinishLaunchingWithOptions: method of almost every application. Listing 3-1 shows a version of this method that installs the view from the application’s main view controller into the application window. Both the window and the view controller are stored in the application’s main nib file, which is loaded before the method is called. However, the view hierarchy managed by the view controller is not actually loaded until the view property is accessed.

几乎每个应用程序的application:didFinishLaunchingWithOptions:方法里都有添加一个子视图到另一个视图的例子。列表 3-1 显示了该方法的一个版本,它从应用程序的主视图控制器载入视图到应用程序窗口。 窗口和视图控制器都存储在应用程序的主nib文件里,它在application:didFinishLaunchingWithOptions:方法被调用之前载入。 然而,视图控制器管理的视图层次结构没有被载入,直到 view 属性被访问。

Listing 3-1  Adding a view to a window

列表 3-1 添加一个视图到窗口

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
 
    // Add the view controller's view to the window and display.
    [window addSubview:viewController.view];
    [window makeKeyAndVisible];
 
    return YES;
}

Another common place where you might add subviews to a view hierarchy is in the loadView or viewDidLoadmethods of a view controller. If you are building your views programmatically, you put your view creation code in theloadView method of your view controller. Whether you create your views programmatically or load them from a nib file, you could include additional view configuration code in the viewDidLoad method.

另一个你可能添加子视图到一个视图层次结构的常见地方是一个视图控制器的loadView 或 viewDidLoad 方法。如果你用程序建立你的视图,你在视图控制器的loadView 方法内输入视图创建代码。 不管你是通过程序创建你的视图还是通过一个nib文件载入视图,你都可以在viewDidLoad方法里包含额外的视图配置代码。

Listing 3-2 shows the viewDidLoad method of the TransitionsViewController class from the UICatalog sample application. The TransitionsViewController class manages the animations associated with transitioning between two views. The application’s initial view hierarchy (consisting of a root view and toolbar) is loaded from a nib file. The code in the viewDidLoad method subsequently creates the container view and image views used to manage the transitions. The purpose of the container view is to simplify the code needed to implement the transition animations between the two image views. The container view has no real content of its own.

列表 3-2 显示了UICatalog 例子应用程序的 TransitionsViewController类的viewDidLoad 方法。  TransitionsViewController类管理2个视图之间相关过渡的各种动画。应用程序的初始视图层次结构(由一个根视图和工具栏组成)是从一个nib文件载入的。然后viewDidLoad方法里的代码创建了用来管理各种过渡的容器视图和图像视图。 容器视图的目标是简化实现两个图像视图之间过渡所需的代码。容器视图本身没有任何真实内容。

Listing 3-2  Adding views to an existing view hierarchy

列表 3-2 添加视图到一个已经存在的视图层次结构

- (void)viewDidLoad
{
    [super viewDidLoad];
 
    self.title = NSLocalizedString(@"TransitionsTitle", @"");
 
    // create the container view which we will use for transition animation (centered horizontally)
    CGRect frame = CGRectMake(round((self.view.bounds.size.width - kImageWidth) / 2.0),
                                                        kTopPlacement, kImageWidth, kImageHeight);
    self.containerView = [[[UIView alloc] initWithFrame:frame] autorelease];
    [self.view addSubview:self.containerView];
 
    // The container view can represent the images for accessibility.
    [self.containerView setIsAccessibilityElement:YES];
    [self.containerView setAccessibilityLabel:NSLocalizedString(@"ImagesTitle", @"")];
 
    // create the initial image view
    frame = CGRectMake(0.0, 0.0, kImageWidth, kImageHeight);
    self.mainView = [[[UIImageView alloc] initWithFrame:frame] autorelease];
    self.mainView.image = [UIImage imageNamed:@"scene1.jpg"];
    [self.containerView addSubview:self.mainView];
 
    // create the alternate image view (to transition between)
    CGRect imageFrame = CGRectMake(0.0, 0.0, kImageWidth, kImageHeight);
    self.flipToView = [[[UIImageView alloc] initWithFrame:imageFrame] autorelease];
    self.flipToView.image = [UIImage imageNamed:@"scene2.jpg"];
}

Important: Superviews automatically retain their subviews, so after embedding a subview it is safe to release that subview. In fact, doing so is recommended because it prevents your application from retaining the view one time too many and causing a memory leak later. Just remember that if you remove a subview from its superview and intend to reuse it, you must retain the subview again. The removeFromSuperview method autoreleases a subview before removing it from its superview. If you do not retain the view before the next event loop cycle, the view will be released.

重要提示:父视图自动保留它们的子视图,所以嵌入(embedding)子视图后就可以安全的释放该子视图。 事实上,我们推荐这样做,因为它避免了应用程序一次保留太多视图,而导致内存不足。 只需要记住,如果你从父视图删除一个子视图,并打算重新使用它,你必须再次保留(retain)该子视图。 removeFromSuperview方法在从父视图删除子视图之前先autorelease该子视图。如果你在下一个事件循环之前没有保留该视图,该视图将被释放。

For more information about Cocoa memory management conventions, see Advanced Memory Management Programming Guide.

 更多关于Cocoa内存管理约定的信息,请看Advanced Memory Management Programming Guide.

When you add a subview to another view, UIKit notifies both the parent and child views of the change. If you implement custom views, you can intercept these notifications by overriding one or more of thewillMoveToSuperview:willMoveToWindow:willRemoveSubview:didAddSubview:didMoveToSuperview, ordidMoveToWindow methods. You can use these notifications to update any state information related to your view hierarchy or to perform additional tasks.

当你添加一个子视图到另一个视图时,UIKit把改变同时通知给父视图和子视图。 如果你现实了自定义视图,你可以侦听(intercept)这些通知,通过重载一个或多个以下方法:willMoveToSuperview:willMoveToWindow:willRemoveSubview:didAddSubview:didMoveToSuperview, 或 didMoveToWindow。你可以使用这些通知来更新任何跟视图层次结构相关的状态信息,或来执行额外的任务。

After creating a view hierarchy, you can navigate it programmatically using the superview and subviews properties of your views. The window property of each view contains the window in which that view is currently displayed (if any). Because the root view in a view hierarchy has no parent, its superview property is set to nil. For views that are currently onscreen, the window object is the root view of the view hierarchy.

创建完一个视图层次结构之后,你可以通过程序使用视图的 superview 和 subviews 属性来使用它。每个视图的 window 属性包含了该窗口,窗口里显示当前的所有视图(如果有)。因为视图层次结构里的根视图没有父视图,它的superview属性被设置为nil. 对于那些正在屏幕上的所有视图,窗口对象是该视图层次结构里的根视图。

Hiding Views

2、隐藏视图

To hide a view visually, you can either set its hidden property to YES or change its alpha property to 0.0. A hidden view does not receive touch events from the system. However, hidden views do participate in autoresizing and other layout operations associated with the view hierarchy. Thus, hiding a view is often a convenient alternative to removing views from your view hierarchy, especially if you plan to show the views again at some point soon.

要想隐藏一个视图,你可以设置它的hidden 为YES, 也可以改变它的alpha 属性为0.0。隐藏视图不能接收系统的触摸事件。 然而, 隐藏视图确实也参与跟视图层次结构相关的自动尺寸以及其它层操作。 因此,隐藏视图常常是从视图层次结构删除视图的一个方便的替代方法,尤其是当你想在一些点很快的再次显示这些视图时。

Important: If you hide a view that is currently the first responder, the view does not automatically resign its first responder status. Events targeted at the first responder are still delivered to the hidden view. To prevent this from happening, you should force your view to resign the first responder status when you hide it. For more information about the responder chain, see Event Handling Guide for iOS.

 重要提示: 如果你隐藏了当前的第一响应者视图,该视图不会自动放弃(resign)他的第一响应者状态。那些目标是该第一响应者的事件任然被传递到该隐藏视图。 为了避免发生这现象,你应该在隐藏视图时强制视图放弃他的第一响应者状态。 关于响应者链的更多信息,请看 Event Handling Guide for iOS.

If you want to animate a view’s transition from visible to hidden (or the reverse), you must do so using the view’salpha property. The hidden property is not an animatable property, so any changes you make to it take effect immediately.

如果你想要动画视图从可见到隐藏的过渡(或相反),你必须用视图的alpha 属性来完成。 hidden属性不是一个可动画的属性,所以你对它的任何改变都将立即发生效果。

Locating Views in a View Hierarchy

3、在视图层次结构里定位视图

There are two ways to locate views in a view hierarchy:

有两种方法可以在一个视图层次结构里定位视图:

  • Store pointers to any relevant views in an appropriate location, such as in the view controller that owns the views.

    在一个合适的位置在任何相关的视图里存储指针,比如在拥有这些视图的视图控制器里。

  • Assign a unique integer to each view’s tag property and use the viewWithTag: method to locate it.

    给每个视图的tag属性分配一个单独的整数值,然后使用viewWithTag: 方法来定位它。

Storing references to relevant views is the most common approach to locating views and makes accessing those views very convenient. If you used Interface Builder to create your views, you can connect objects in your nib file (including the File’s Owner object that represents the managing controller object) to one another using outlets. For views you create programmatically, you can store references to those views in private member variables. Whether you use outlets or private member variables, you are responsible for retaining the views as needed and then releasing them as well. The best way to ensure objects are retained and released properly is to use declared properties.

存储指向相关视图的引用是定位视图最常见的方法,它能很方便的访问那些视图。 如果你使用界面生成器来创建你的视图,你可以在nib文件里使用接口(outlets)连接对象到另一个对象, nib文件包含代表管理控制器对象的File's Owner 对象。对于你用程序创建的各个视图,你可以在似有成员变量里存储指向那些视图的引用指针。 不管你是使用接口还是似有成员变量,你都得根据需要负责保留这些视图,以及在以后负责释放它们。 确保对象已经被正确保留和释放的最好方法是使用声明的属性(declared properties)。

Tags are a useful way to reduce hard-coded dependencies and support more dynamic and flexible solutions. Rather than storing a pointer to a view, you could locate it using its tag. Tags are also a more persistent way of referring to views. For example, if you wanted to save the list of views that are currently visible in your application, you would write out the tags of each visible view to a file. This is simpler than archiving the actual view objects, especially in situations where you are tracking only which views are currently visible. When your application is subsequently loaded, you would then re-create your views and use the saved list of tags to set the visibility of each view, and thereby return your view hierarchy to its previous state.

标签(tags)是一个有用的方法来减少对硬性编码的依赖性,并支持更多动态灵活(flexible)的解决办法。 替代存储一个指向视图的指针,你可以使用它的标签来定位它。 标签也是一种引用视图的更加持久的方法。比如,如果你想要存储一个应用程序中正可见的视图列表,你可以把每个可见视图的标签写入一个文件。 这样比存档实际视图对象更简单,特别是在你只监控哪些视图在当前可见的情况下。 然后当你的应用程序被载入时,你可以使用这些被保存的标签列表来重新创建你的视图,并设置它们的可见性(visibility), 从而把你的视图层次结构返回到它的前一个状态。

Translating, Scaling, and Rotating Views

4、 转化,缩放,以及旋转视图

Every view has an associated affine transform that you can use to translate, scale, or rotate the view’s content. View transforms alter the final rendered appearance of the view and are often used to implement scrolling, animations, or other visual effects.

每个视图都有一个相关的反射变换,你可以用它来转化,缩放,或旋转视图的内容。视图变换(transforms)改变视图最后的渲染外形,并被常常用来实现滚动,动画,或其它可视效果。

The transform property of UIView contains a CGAffineTransform structure with the transformations to apply. By default, this property is set to the identity transform, which does not modify the appearance of the view. You can assign a new transform to this property at any time. For example, to rotate a view by 45 degrees, you could use the following code:

UIView的transform 属性包含了一个可以申请各种转换的CGAffineTransform 结构。 默认情况下,该属性被设置为恒等变换(identity transform), 它不修改视图的外形。 你可以在任何时候给该属性一个新值。 比如,旋转视图45度,你可以使用以下代码:

// M_PI/4.0 is one quarter of a half circle, or 45 degrees.
CGAffineTransform xform = CGAffineTransformMakeRotation(M_PI/4.0);
self.view.transform = xform;

Applying the transform in the preceding code to a view would rotate that view clockwise about its center point. Figure 3-2 shows how this transformation would look if it were applied to an image view embedded in an application.

给视图应用以上代码的变换可以将视图围绕它的中心点旋转45度。图3-2 显示了该变换应用于突变视图的效果。

Figure 3-2  Rotating a view 45 degrees

图 3-2 旋转视图45度

When applying multiple transformations to a view, the order in which you add those transformations to theCGAffineTransform structure is significant. Rotating the view and then translating it is not the same as translating the view and then rotating it. Even if the amounts of rotation and translation are the same in each case, the sequence of the transformations affects the final results. In addition, any transformations you add are applied to the view relative to its center point. Thus, applying a rotation factor rotates the view around its center point. Scaling a view changes the width and height of the view but does not change its center point.

当你给视图应用多个变换时,变换按你添加这些变换的顺序执行。旋转视图然后转化它跟转化视图然后旋转它不一样。即使在每个情况下旋转和转化的数目都一样, 变换序列影响最终结果。 另外, 任何你添加的变换都根据它的中心点被应用于视图。 因此, 应用一个旋转因子,让视图绕着它的中心点旋转。 缩放视图改变视图的长和宽,但是不改变它的中心点。

For more information about creating and using affine transforms, see “Transforms” in Quartz 2D Programming Guide.

更多关于创建和使用反射变换的信息,请看Quartz 2D Programming Guide 里的“Transforms”

Converting Coordinates in the View Hierarchy

5、 在视图层次结构里转换坐标

At various times, particularly when handling events, an application may need to convert coordinate values from one frame of reference to another. For example, touch events report the location of each touch in the window’s coordinate system but view objects often need that information in the view’s local coordinate system. The UIView class defines the following methods for converting coordinates to and from the view’s local coordinate system:

在不同的事件,特别是当处理事件时,应用程序可能需要转换坐标值,从一个框架引用到另一个。 比如,触摸事件报告每个触摸在窗口坐标系统里的位置,但是视图对象常常需要该位置在视图的内部坐标系统中的值。 UIView 类定义了以下方法,用来从视图的内部坐标系统转换坐标或从别的坐标系统转换到视图的内部坐标系统:

The convert...:fromView: methods convert coordinates from some other view’s coordinate system to the local coordinate system (bounds rectangle) of the current view. Conversely, the convert...:toView: methods convert coordinates from the current view’s local coordinate system (bounds rectangle) to the coordinate system of the specified view. If you specify nil as the reference view for any of the methods, the conversions are made to and from the coordinate system of the window that contains the view.

convert...:fromView: 方法把其它视图坐标系的坐标转换为当前视图的内部坐标系(边界矩形)坐标。相反,convert...:toView:方法则把当前坐标系坐标转换为制定的视图坐标系坐标。 如果你给任何方法指定了nil值作为引用视图,该转换是包含该视图的窗口坐标系统中转换而来,或者是转换到该窗口坐标系统中去。

In addition to the UIView conversion methods, the UIWindow class also defines several conversion methods. These methods are similar to the UIView versions except that instead of converting to and from a view’s local coordinate system, these methods convert to and from the window’s coordinate system.

除了UIView 转换方法,UIWindow 类也定义了一些转换方法。这些方法跟UIView的方法相似,只除了这些方法是从窗口坐标系统中转换而来,或者转换到窗口坐标系统中去。

When converting coordinates in rotated views, UIKit converts rectangles under the assumption that you want the returned rectangle to reflect the screen area covered by the source rectangle. Figure 3-3 shows an example of how rotations can cause the size of the rectangle to change during a conversion. In the figure, an outer parent view contains a rotated subview. Converting a rectangle in the subview’s coordinate system to the parent’s coordinate system yields a rectangle that is physically larger. This larger rectangle is actually the smallest rectangle in the bounds of outerView that completely encloses the rotated rectangle.

当你在被旋转的视图里转换坐标时,UIKit在这样的假设下转换矩形---假设你想要返回的矩形反映被源矩形覆盖的屏幕区域。图3-3 显示了一个例子---在转换过程中旋转如何导致矩形尺寸的改变。在图中,一个外部父视图包含了一个已旋转的子视图。转换在子视图坐标系统里的一个子视图到父视图坐标系统,生成了一个外形上更大的矩形。该被放大的矩形实际上是在外部视图(outerView)边界里完全包含被旋转矩形的最小矩形。

Figure 3-3  Converting values in a rotated view

图 3-3 在一个已旋转的视图里转换值

Converting values in a rotated view

Adjusting the Size and Position of Views at Runtime

三、在运行时调整视图的尺寸和位置

Whenever the size of a view changes, the size and position of its subviews must change accordingly. The UIView class supports both the automatic and manual layout of views in a view hierarchy. With automatic layout, you set the rules that each view should follow when its parent view resizes, and then forget about resizing operations altogether. With manual layout, you manually adjust the size and position of views as needed.

无论何时一个视图的尺寸发生了改变,它的子视图的尺寸和位置也必须相应的发生改变。UIView 类同时支持在一个视图层次结构里的视图的自动和手动布局。 在自动布局里,你设置这样的规则:每个视图当它的父视图重新调整尺寸时都应该跟着改变,然后再一起忘记关于重新调整尺寸的操作。对于手动布局,你根据需要手动调整视图的尺寸和位置。

Being Prepared for Layout Changes

1、随时为布局的改变做准备

Layout changes can occur whenever any of the following events happens in a view:

布局改变在以下事件发生时随时发生:

  • The size of a view’s bounds rectangle changes.

    视图的边界矩形的尺寸发生改变。

  • An interface orientation change occurs, which usually triggers a change in the root view’s bounds rectangle.

    界面方向发生改变,该改变往往由根目录的边界矩形内的一个改变促发。

  • The set of Core Animation sublayers associated with the view’s layer changes and requires layout.

    跟视图层相关的内核动画子层集合发生变化并要求布局(layout)。

  • Your application forces layout to occur by calling the setNeedsLayout or layoutIfNeeded method of a view.

    应用程序通过调用视图的setNeedsLayout 或 layoutIfNeeded 方法强制布局发生改变。

  • Your application forces layout by calling the setNeedsLayout method of the view’s underlying layer object.

    应用程序调用视图的底层层对象的setNeedsLayout方法来强制布局发生变化。

Handling Layout Changes Automatically Using Autoresizing Rules

2、使用自动调整尺寸规则自动处理布局的变化

When you change the size of a view, the position and size of any embedded subviews usually needs to change to account for the new size of their parent. The autoresizesSubviews property of the superview determines whether the subviews resize at all. If this property is set to YES, the view uses the autoresizingMask property of each subview to determine how to size and position that subview. Size changes to any subviews trigger similar layout adjustments for their embedded subviews.

当你改变了视图的尺寸,所有子视图通常都需要改变他们的位置和尺寸,以适应它们父视图的新尺寸。 父视图的 autoresizesSubviews 属性完全决定子视图是否需要重新调整尺寸。 如果该属性被设置为YES, 视图使用每个子视图的autoresizingMask 属性来决定如何调整该子视图的尺寸和位置。 任何子视图的尺寸发生改变同时又触发它们的子视图也跟着改变以适应变化。

For each view in your view hierarchy, setting that view’s autoresizingMask property to an appropriate value is an important part of handling automatic layout changes. Table 3-2 lists the autoresizing options you can apply to a given view and describes their effects during layout operations. You can combine constants using an OR operator or just add them together before assigning them to the autoresizingMask property. If you are using Interface Builder to assemble your views, you use the Autosizing inspector to set these properties.

对于视图层次结构里的每个视图,给那个视图的 autoresizingMask 属性设置一个合适的值是处理自动布局改变的一个很重要的部分。你可以使用OR运算符组合这些常量或只需要在把它们发送给autoresizingMask属性之前把它们加到一起。如果你使用界面生成器来组合(assemble)你的视图,你可以使用Autosizing inspector 来设置这些属性。

Table 3-2  Autoresizing mask constants

Autoresizing mask

Description

UIViewAutoresizingNone

The view does not autoresize. (This is the default value.)

视图不会自动调整尺寸(这是默认值)

UIViewAutoresizingFlexibleHeight

The view’s height changes when the superview’s height changes. If this constant is not included, the view’s height does not change.
当父视图的告诉发生改变时,视图的高度也跟着改变

UIViewAutoresizingFlexibleWidth

The view’s width changes when the superview's width changes. If this constant is not included, the view’s width does not change.

当父视图的宽度发生改变时,该视图的宽度也发生改变。

UIViewAutoresizingFlexibleLeftMargin

The distance between the view’s left edge and the superview’s left edge grows or shrinks as needed. If this constant is not included, the view’s left edge remains a fixed distance from the left edge of the superview.

视图左边缘和父视图的左边缘之间的距离根据需要放大或缩小。如果不包含该常量,视图的左边缘跟父视图的左边缘之间的距离保持一个固定值。

UIViewAutoresizingFlexibleRightMargin

The distance between the view’s right edge and the superview’s right edge grows or shrinks as needed. If this constant is not included, the view’s right edge remains a fixed distance from the right edge of the superview.

视图右边缘和父视图的右边缘之间的距离根据需要放大或缩小。如果不包含该常量,视图的右边缘跟父视图的右边缘之间的距离保持一个固定值。

UIViewAutoresizingFlexibleBottomMargin

The distance between the view’s bottom edge and the superview’s bottom edge grows or shrinks as needed. If this constant is not included, the view’s bottom edge remains a fixed distance from the bottom edge of the superview.

视图下边缘和父视图的下边缘之间的距离根据需要放大或缩小。如果不包含该常量,视图的下边缘跟父视图的下边缘之间的距离保持一个固定值。

UIViewAutoresizingFlexibleTopMargin

The distance between the view’s top edge and the superview’s top edge grows or shrinks as needed. If this constant is not included, the view’s top edge remains a fixed distance from the top edge of the superview.

视图上边缘和父视图的上边缘之间的距离根据需要放大或缩小。如果不包含该常量,视图的上边缘跟父视图的上边缘之间的距离保持一个固定值。

Figure 3-4 shows a graphical representation of how the options in the autoresizing mask apply to a view. The presence of a given constant indicates that the specified aspect of the view is flexible and may change when the superview’s bounds change. The absence of a constant indicates that the view’s layout is fixed in that aspect. When you configure a view that has more than one flexible attribute along a single axis, UIKit distributes any size changes evenly among the corresponding spaces.

图3-4 显示了这些自动调整尺寸蒙版(autoresizing mask)应用于视图之后的图形化效果。给定常量的存在表明视图的特定方面是灵活的并可能在父视图边界发生变化时跟着改变。没有指定常量表明视图的布局在这方面是固定的。当你沿着一个单轴(single axis)配置拥有多个灵活的属性的视图时,UIKit把所有的尺寸变化均匀地分布在相关空间里。

Figure 3-4  View autoresizing mask constantsView autoresizing mask constants

The easiest way to configure autoresizing rules is using the Autosizing controls in the Size inspector of Interface Builder. The flexible width and height constants from the preceding figure have the same behavior as the width and size indicators in the Autosizing controls diagram. However, the behavior and use of margin indicators is effectively reversed. In Interface Builder, the presence of a margin indicator means that the margin has a fixed size and the absence of the indicator means the margin has a flexible size. Fortunately, Interface Builder provides an animation to show you how changes to the autoresizing behaviors affect your view.

配置自动调整尺寸最简单的方法是使用界面生成器中Size inspector的Autosizing controls项。上图中灵活的长宽常量跟在Autosizing controls图中的width and size indicators 有着相同的行为。然而, margin indicators的行为和使用却相反。在界面生成器里,margin indicator的存在表明一个固定值,不存在该值则表明margin有一个灵活的值。 幸运的是,界面生成器提供了一个动画,用来给你显示如何改变autoresizing 行为才会对你的视图产生影响。

Important: If a view’s transform property does not contain the identity transform, the frame of that view is undefined and so are the results of its autoresizing behaviors.

 重要提示: 如果视图的transform 属性值不是恒等变换,该视图的frame是未定义的,而且它的自动调整尺寸行为的结果也是未定义的。

After the automatic autoresizing rules for all affected views have been applied, UIKit goes back and gives each view a chance to make any necessary manual adjustments to its superview. For more information about how to manage the layout of views manually, see “Tweaking the Layout of Your Views Manually.”

当对所有被影响的视图应用自动调整尺寸规则之后,UIKit返回并给每个视图一个机会来做针对其父视图的任何必要的手动调整。关于如何手动管理视图的布局的更多信息,请看“Tweaking the Layout of Your Views Manually.”

Tweaking the Layout of Your Views Manually

3、 手动调整你的视图布局

Whenever the size of a view changes, UIKit applies the autoresizing behaviors of that view’s subviews and then calls the layoutSubviews method of the view to let it make manual changes. You can implement the layoutSubviewsmethod in custom views when the autoresizing behaviors by themselves do not yield the results you want. Your implementation of this method can do any of the following:

任何时候当视图的尺寸发生变化时,UIKit给那个视图的子视图应用自动调整尺寸行为,然后调用视图的layoutSubviews方法来让它做手动改变。当它们的自动调整尺寸行为不能产生你想要的结构时,你可以在自定义视图里实现layoutSubviews方法。你对该方法的实现可以完成以下任何事情:

  • Adjust the size and position of any immediate subviews.

    调整任何临近子视图的尺寸和位置。

  • Add or remove subviews or Core Animation layers.

    添加或删除子视图或内核动画层。

  • Force a subview to be redrawn by calling its setNeedsDisplay or setNeedsDisplayInRect: method.

    通过调用 setNeedsDisplay 或 setNeedsDisplayInRect: 方法来强制子视图重新绘制。

One place where applications often lay out subviews manually is when implementing a large scrollable area. Because it is impractical to have a single large view for its scrollable content, applications often implement a root view that contains a number of smaller tile views. Each tile represents a portion of the scrollable content. When a scroll event happens, the root view calls its setNeedsLayout method to initiate a layout change. Its layoutSubviews method then repositions the tile views based on the amount of scrolling that occurred. As tiles scroll out of the view’s visible area, the layoutSubviews method moves the tiles to the incoming edge, replacing their contents in the process.

应用程序常常手动布局子视图的地方时当你实现一个大的可滚动区域时。因为只给它的滚动区域设置一个大的单一视图是不切实际的,应用程序常常实现一个根视图,在根视图里包含一定数量的较小的瓷砖(tile)视图。每个瓷砖代表可滚动内容的一部分。 当一个滚动事件发生时,根视图调用它的setNeedsLayout 方法来初始化一个布局改变。 然后它的layoutSubviews方法在发生的滚动数量基础上重新定位这些瓷砖视图。 当瓷砖滚出了视图的可见区域时,layoutSubviews方法把这些瓷砖移到进入的边缘,在进程中替换它们的内容。

When writing your layout code, be sure to test your code in the following ways:

当你编写你的布局代码时,请确保你的代码通过了以下测试:

  • Change the orientation of your views to make sure the layout looks correct in all supported interface orientations.

    改变你的视图方向,确保布局在所有支持的界面方向上都显示正确。

  • Make sure your code responds appropriately to changes in the height of the status bar. When a phone call is active, the status bar height increases in size, and when the user ends the call, the status bar decreases in size.

    确保你的代码能对状态栏高度的变化做出适当的响应。 当有来电时,状态栏高度尺寸增大,当用户切断通话时,状态栏缩减它的尺寸。

For information about how autoresizing behaviors affect the size and position of your views, see “Handling Layout Changes Automatically Using Autoresizing Rules.” For an example of how to implement tiling, see the ScrollViewSuitesample.

关于自定调整尺寸行为如何影响视图的尺寸和位置的信息,请看“Handling Layout Changes Automatically Using Autoresizing Rules.” 关于如何实现tiling的例子,请看ScrollViewSuite

Modifying Views at Runtime

四、在运行时修改视图

As applications receive input from the user, they adjust their user interface in response to that input. An application might modify its views by rearranging them, changing their size or position, hiding or showing them, or loading an entirely new set of views. In iOS applications, there are several places and ways in which you perform these kinds of actions:

当应用程序接收到用户输入时,它们调整它们的用户界面来响应那个输入。应用程序可能通过重新整理,改变它们的尺寸或位置,隐藏或显示它们,或载入一个完整的视图新集来修改它们的视图。在iOS应用程序中,有很多地方和很多方法可以让你执行这类操作:

  • In a view controller:

     在视图控制器中:

    • A view controller has to create its views before showing them. It can load the views from a nib file or create them programmatically. When those views are no longer needed, it disposes of them.

       视图控制器必须在显示它们之前创建它的视图。它能从一个nib文件载入视图,或者通过程序创建它们。当这些视图不再需要时,它负责丢弃它们。

    • When a device changes orientations, a view controller might adjust the size and position of views to match. As part of its adjustment to the new orientation, it might hide some views and show others.

       当设备改变它的方向时,视图控制器可能调整视图的尺寸和位置来匹配该方向。作为应对新方向做出调整的一部分,它可能隐藏一些视图和显示一些其它视图。

    • When a view controller manages editable content, it might adjust its view hierarchy when moving to and from edit mode. For example, it might add extra buttons and other controls to facilitate editing various aspects of its content. This might also require the resizing of any existing views to accommodate the extra controls.

       当视图控制器管理一个可编辑内容时,当移入或移出编辑模式时,它可能调整它的视图层次结构。比如,它可能添加额外的按钮和其它一些控件来方便编辑它内容的各个方面。 这样也可能要求对任何已经存在的视图做尺寸调整以容纳额外的控件。

  • In animation blocks:

     在动画块中:

    • When you want to transition between different sets of views in your user interface, you hide some views and show others from inside an animation block.

       当你想要在用户界面上的不同视图集之间互相过渡时,你从一个动画块里隐藏一些视图和显示其它视图。

    • When implementing special effects, you might use an animation block to modify various properties of the view. For example, to animate changes to the size of a view, you would change the size of its frame rectangle.

       当你想实现一些特殊效果时,你可能需要使用一个动画块来修改视图中的各种属性。比如,动画视图的尺寸变化,你可能改变它的框架矩形尺寸。

  • Other ways:

    其它方式: 

    • When touch events or gestures occur, your interface might respond by loading a new set of views or changing the current set of views. For information about handling events, see Event Handling Guide for iOS.

       当触摸事件或手势发生时,你的界面可能通过载入一个新的视图集或改变当前的视图集来响应。 关于处理事件的信息,请看Event Handling Guide for iOS.

    • When the user interacts with a scroll view, a large scrollable area might hide and show tile subviews. For more information about supporting scrollable content, see Scroll View Programming Guide for iOS.

       当用户跟一个滚动视图交互时,一个巨大的可滚动区可能隐藏,并显示瓷砖子视图。关于支持可滚动内容的更多信息,请看Scroll View Programming Guide for iOS.

    • When the keyboard appears, you might reposition or resize views so that they do not lie underneath the keyboard. For information about how to interact with the keyboard, see Text, Web, and Editing Programming Guide for iOS.

       当键盘出现时,你可能对视图做重新定位和定尺寸,这样它们在不会被隐藏在键盘下面。 关于如何跟键盘交互的信息,请看Text, Web, and Editing Programming Guide for iOS

View controllers are a common place to initiate changes to your views. Because a view controller manages the view hierarchy associated with the content being displayed, it is ultimately responsible for everything that happens to those views. When loading its views or handling orientation changes, the view controller can add new views, hide or replace existing ones, and make any number of changes to make the views ready for the display. And if you implement support for editing your view’s content, the setEditing:animated: method in UIViewController gives you a place to transition your views to and from their editable versions.

视图控制器是开始改变视图的一个常用的地方。因为一个视图控制器管理着视图层次,该层次跟要显示的内容相关联,它最终负责对那些视图发生的所有事情。 当载入它的视图或处理方向变换时,视图控制器能添加新视图,隐藏或替换存在的视图,并对准备显示的视图做任何数量的改变。 并且如果你实现支持可编辑视图的内容, UIViewController里的setEditing:animated: 方法给你提供了一个地方,让你的视图从它们可编辑版本中过渡过来,或从可编辑版本过渡到视图中去。

Animation blocks are another common place to initiate view-related changes. The animation support built into theUIView class makes it easy to animate changes to view properties. You can also use thetransitionWithView:duration:options:animations:completion: ortransitionFromView:toView:duration:options:completion: methods to swap out entire sets of views for new ones.

动画块是开始做视图相关改变的另一个常用地方。 支持在UIView类里内建动画让动画视图属性的各种改变变得容易。你还可以使用transitionWithView:duration:options:animations:completion: 或 transitionFromView:toView:duration:options:completion: 方法来给新的视图集合换出整个视图集合。

For more information about animating views and initiating view transitions, see “Animations.” For more information on how you use view controllers to manage view-related behaviors, see View Controller Programming Guide for iOS.

关于动画视图和启动视图过渡的更多信息,请看 “Animations.” 关于你如何使用视图控制器来管理视图相关的行为的更多信息,请看View Controller Programming Guide for iOS.

Interacting with Core Animation Layers

五、跟内核动画层交互

Each view object has a dedicated Core Animation layer that manages the presentation and animation of the view’s content on the screen. Although you can do a lot with your view objects, you can also work directly with the corresponding layer objects as needed. The layer object for the view is stored in the view’s layer property.

每个视图对象都有一个专用的内核动画层,该动画层管理屏幕上视图内容的呈现和动画。 尽管你可以用视图对象完成很多事情,你也可以根据需要直接跟相关的层对象一起工作。 视图的层对象被存储在视图的layer 属性里。

Changing the Layer Class Associated with a View

1、更改跟视图相关联的层类

The type of layer associated with a view cannot be changed after the view is created. Therefore, each view uses thelayerClass class method to specify the class of its layer object. The default implementation of this method returns the CALayer class and the only way to change this value is to subclass, override the method, and return a different value. You might want to change this value in the following circumstances:

跟视图相关联的层类型在视图被创建后就不能被更改。 因此,每次视图使用layerClass 类方法来指定它层对象的类。该方法的默认实现返回CALayer 类,而且改变该值的唯一方法是用于它的子类,重载该方法,并返回一个不同值。你可能想要在以下情况时改变该值:

  • Your application uses OpenGL ES for drawing, in which case, the layer must be an instance of the CAEAGLLayerclass.

    你的应用程序使用 OpenGL ES来绘图,在这种情况下,层必须是 CAEAGLLayer 类的一个实例。

  • Your view uses tiling to display a large scrollable area, in which case you might want to use the CATiledLayerclass to back your view instead.

     你的视图使用了tiling来显示一个巨大的可滚动区域, 在这种情况下你可能想使用CATiledLayer 来替换原来在视图后面的层。

Implementation of the layerClass method should simply create the desired Class object and return it. For example, a view that supports OpenGL ES drawing would have the following implementation for this method:

实现layerClass 方法应该简单创建一个所需的Class 对象,并返回它。比如,一个支持OpenGL ES绘图的视图可能对该方法做以下实现:

+ (Class)layerClass
{
    return [CAEAGLLayer class];
}

Each view calls its layerClass method early in its initialization process and uses the returned class to create its layer object. In addition, the view always assigns itself as the delegate of its layer object. At this point, the view owns its layer and the relationship between the view and layer must not change. You must also not assign the same view as the delegate of any other layer object. Changing the ownership or delegate relationships of the view will cause drawing problems and potential crashes in your application.

每个视图在它的初始化过程早期调用它的layerClass方法,并使用它返回的类来创建它的层对象。 另外,视图总是作为它的层对象的代理。 在这时,视图拥有它的层,并且视图和层之间的关系决不能改变。你也决不能让同一个视图成为任何其它层对象的代理。改变视图的所属关系或代理关系将导致应用程序发生绘图问题以及潜在崩溃。

For more information about the different types of layer objects provided by Core Animation, see Core Animation Reference Collection.

关于由内核动画提供的层对象的不同类型的更多信息,请看Core Animation Reference Collection.

Embedding Layer Objects in a View

2、在视图里嵌入层对象

If you prefer to work primarily with layer objects instead of views, you can incorporate custom layer objects into your view hierarchy as needed. A custom layer object is any instance of CALayer that is not owned by a view. You typically create custom layers programmatically and incorporate them using Core Animation routines. Custom layers do not receive events or participate in the responder chain but do draw themselves and respond to size changes in their parent view or layer according to the Core Animation rules.

如果你更喜欢主要使用层对象来代替视图工作,你可以根据需要在你的视图层次结构里纳入自定义层对象。自定义层对象是任何CALayer 的实例,它不属于任何视图。你通常通过程序创建自定义视图,并用内核动画程序纳入它们。自定义层不接收事件或参与响应链,但是它们确实绘制它们自己并响应它们父视图或跟内核动画规则相关的层的尺寸改变。

Listing 3-3 shows an example of the viewDidLoad method from a view controller that creates a custom layer object and adds it to its root view. The layer is used to display a static image that is animated. Instead of adding the layer to the view itself, you add it to the view’s underlying layer.

列表3-3 显示了一个例子,该例子是一个视图控制器中的viewDidLoad方法,它创建并添加一个自定义层到它的根视图。该层用于显示一个被动画的静态图片。 不添加该层到视图本身,而是添加到视图下面的层上。

Listing 3-3  Adding a custom layer to a view

列表 3-3 给视图添加一个自定义层

- (void)viewDidLoad {
    [super viewDidLoad];
 
    // Create the layer.
    CALayer* myLayer = [[CALayer alloc] init];
 
    // Set the contents of the layer to a fixed image. And set
    // the size of the layer to match the image size.
    UIImage layerContents = [[UIImage imageNamed:@"myImage"] retain];
    CGSize imageSize = layerContents.size;
 
    myLayer.bounds = CGRectMake(0, 0, imageSize.width, imageSize.height);
    myLayer = layerContents.CGImage;
 
    // Add the layer to the view.
    CALayer*    viewLayer = self.view.layer;
    [viewLayer addSublayer:myLayer];
 
    // Center the layer in the view.
    CGRect        viewBounds = backingView.bounds;
    myLayer.position = CGPointMake(CGRectGetMidX(viewBounds), CGRectGetMidY(viewBounds));
 
    // Release the layer, since it is retained by the view's layer
    [myLayer release];
}

You can add any number of sublayers and arrange them into sublayer hierarchies, if you want. However, at some point, those layers must be attached to the layer object of a view.

如果你需要,你可以添加任何数量的子层并把它们整理到子层层次结构里。然而,在一些时候,那些层必须被连接到一个视图的层对象上。

For information on how to work with layers directly, see Core Animation Programming Guide.

关于如何跟层直接工作的信息,请看Core Animation Programming Guide.

Defining a Custom View

六、定义一个自定义视图

If the standard system views do not do exactly what you need, you can define a custom view. Custom views give you total control over the appearance of your application’s content and how interactions with that content are handled.

如果标准系统视图不是你所需要的,你可以定义一个自定义视图。自定义视图让你完全控制应用程序呢荣的外形,以及如何处理跟那些内容的交互。

Checklist for Implementing a Custom View

1、实现一个自定义视图的清单

The job of a custom view is to present content and manage interactions with that content. The successful implementation of a custom view involves more than just drawing and handling events, though. The following checklist includes the more important methods you can override (and behaviors you can provide) when implementing a custom view:

一个自定义视图的工作是呈现内容并管理与该内容的交互。 但是,一个自定义视图的成功实现不仅仅涉及绘制和处理事件。以下清单包含了更多你实现一个自定义视图时可以重载的重要方法(以及你可以提供的行为):

  • Define the appropriate initialization methods for your view:

     为你的视图定义合适的初始化方法:

    • For views you plan to create programmatically, override the initWithFrame: method or define a custom initialization method.

       对于你计划通过程序创建的视图,重载initWithFrame: 方法,或定义一个自定义初始化方法。

    • For views you plan to load from nib files, override the initWithCoder: method. Use this method to initialize your view and put it into a known state.

       对于你计划从nib文件载入的视图,重载initWithCoder: 方法。使用该方法来初始化你的视图并把它放入已知的状态。

  • Implement a dealloc method to handle the cleanup of any custom data.

     实现一个 dealloc方法来处理任何自定义数据的清理工作。

  • To handle any custom drawing, override the drawRect: method and do your drawing there.

     要想处理任何自定义绘制,重载drawRect:方法,并在那完成绘图。

  • Set the autoresizingMask property of the view to define its autoresizing behavior.

     设置视图的autoresizingMask 属性来定义它的自动调整尺寸行为。

  • If your view class manages one or more integral subviews, do the following:

     如果你的视图类管理一个或多个基本的子视图,完成以下事情:

    • Create those subviews during your view’s initialization sequence.

       在你的视图初始化序列期间创建那些子视图。

    • Set the autoresizingMask property of each subview at creation time.

       在创建时设置每个子视图的autoresizingMask 属性。

    • If your subviews require custom layout, override the layoutSubviews method and implement your layout code there.

      如果你的子视图要求自定义布局,重载 layoutSubviews 方法并在这里实现你的布局代码。

  • To handle touch-based events, do the following:

    要想处理基于触摸的事件,完成以下事情: 

  • If you want the printed version of your view to look different from the onscreen version, implement thedrawRect:forViewPrintFormatter: method. For detailed information about how to support printing in your views, see Drawing and Printing Guide for iOS.

     如果你想让你的视图打印版本跟屏幕上的版本不同,实现drawRect:forViewPrintFormatter: 方法。 关于如何在视图里支持打印的详情,请看Drawing and Printing Guide for iOS.

In addition to overriding methods, remember that there is a lot you can do with the view’s existing properties and methods. For example, the contentMode and contentStretch properties let you change the final rendered appearance of your view and might be preferable to redrawing the content yourself. In addition to the UIView class itself, there are many aspects of a view’s underlying CALayer object that you can configure directly or indirectly. You can even change the class of the layer object itself (which you must do if you plan to use OpenGL ES to draw your view’s content).

除了重载方法,请记住你可以用视图已经存在的属性和方法来完成很多事情。比如, contentMode 和 contentStretch 属性让你改变视图的最终渲染外形,并可能是你自己重新绘制内容更好。除了UIView本身,视图的后台CALayer对象有很多方面你可以直接或间接配置。你甚至可以自己改变层对象的类(如果你想使用OpenGL ES来绘制你的视图内容,你必须这么做)。

For more information about the methods and properties of the view class, see UIView Class Reference.

关于视图类的方法和属性的更多信息,请看UIView Class Reference.

Initializing Your Custom View

2、初始化你的自定义视图

Every new view object you define should include a custom initWithFrame: initializer method. This method is responsible for initializing the class at creation time and putting your view object into a known state. You use this method when creating instances of your view programmatically in your code.

你定义的每个新视图对象都包含一个自定义的initWithFrame: 初始化方法。该方法负责在创建时自定义该类并把视图对象放入一个已知的状态。当你在代码里通过程序创建视图的实例时,你将使用该方法。

Listing 3-4 shows a skeletal implementation of a standard initWithFrame: method. This method calls the inherited implementation of the method first and then initializes the instance variables and state information of the class before returning the initialized object. Calling the inherited implementation is traditionally performed first so that if there is a problem, you can abort your own initialization code and return nil.

列表 3-4 显示了一个标准initWithFrame:方法的框架实现。该方法首先调用该方法的被继承(super)实现,然后在返回初始化对象之前初始化实例变量和类的状态信息。调用被继承的实现通常都首先被执行,这样如果有问题,你可以退出你自己的初始化代码并返回nil。

Listing 3-4  Initializing a view subclass

列表 3-5 初始化一个视图子类

- (id)initWithFrame:(CGRect)aRect {
    self = [super initWithFrame:aRect];
    if (self) {
          // setup the initial properties of the view
          ...
       }
    return self;
}

If you plan to load instances of your custom view class from a nib file, you should be aware that in iOS, the nib-loading code does not use the initWithFrame: method to instantiate new view objects. Instead, it uses theinitWithCoder: method that is part of the NSCoding protocol.

如果你计划从一个nib文件载入你的自定义视图实例,你应该意识到在iOS里,nib载入代码不适用initWithFrame:方法来实例化新的视图对象。作为替代,它使用initWithCoder: 方法,该方法是NSCoding 方法的一部分。

Even if your view adopts the NSCoding protocol, Interface Builder does not know about your view’s custom properties and therefore does not encode those properties into the nib file. As a result, your own initWithCoder: method should perform whatever initialization code it can to put the view into a known state. You can also implement theawakeFromNib method in your view class and use that method to perform additional initialization.

即使你的视图采用了NSCoding协议,界面生成器也不会知道你的视图的自定义属性,因此没有把那些属性编码到nib文件。结果,你拥有的initWithCoder: 方法应该执行任何它能把视图放入一个已知状态的初始化代码。 你还可以在你的视图类里实现awakeFromNib 方法并使用该方法来执行额外的初始化。

Implementing Your Drawing Code

3、实现你的绘制代码

For views that need to do custom drawing, you need to override the drawRect: method and do your drawing there. Custom drawing is recommended only as a last resort. In general, if you can use other views to present your content, that is preferred.

对于那些需要完成自定义绘制的视图,你需要重载 drawRect: 方法,并早那里实现你的绘制。自定义绘制建议只能作为最后的手段。 通常,如果你可以使用其它视图来呈现你的内容,那样做更好。

The implementation of your drawRect: method should do exactly one thing: draw your content. This method is not the place to be updating your application’s data structures or performing any tasks not related to drawing. It should configure the drawing environment, draw your content, and exit as quickly as possible. And if your drawRect:method might be called frequently, you should do everything you can to optimize your drawing code and draw as little as possible each time the method is called.

你的drawRect: 方法实现应该只做好一件事:绘制你的内容。该方法不是更新你的应用程序数据结构或执行任何跟绘制无关的任务。它应该配置绘图环境,绘制内容,以及尽可能快的退出来。并且如果你的drawRect:方法会经常被调用,你应该做你能做的一切来优化你的绘图代码,让它每次调用时尽可能少的绘图。

Before calling your view’s drawRect: method, UIKit configures the basic drawing environment for your view. Specifically, it creates a graphics context and adjusts the coordinate system and clipping region to match the coordinate system and visible bounds of your view. Thus, by the time your drawRect: method is called, you can begin drawing your content using native drawing technologies such as UIKit and Core Graphics. You can get a pointer to the current graphics context using the UIGraphicsGetCurrentContext function.

当你调用视图的drawRect: 方法之前,UIKit为你的视图配置了基本的绘图环境。 特别是,它创建了一个图形上下文以及调整了坐标系统并裁减区域让其跟坐标系相匹配,让视图的边界可见。 因此,当drawRect: 方法被调用时,你可以使用本地绘图技术比如UIKit和Core Graphics开始绘制你的内容。你可以用UIGraphicsGetCurrentContext 函数获取一个指向当前图形上下文的指针。

Important: The current graphics context is valid only for the duration of one call to your view’s drawRect:method. UIKit might create a different graphics context for each subsequent call to this method, so you should not try to cache the object and use it later.

 重要提示:当前图形上下文只在调用视图的drawRect: 方法期间有效。 UIKit可能在每次调用此方法创建一个不同的图形上下文,因此你不应该尝试缓存该对象并在以后使用。

Listing 3-5 shows a simple implementation of a drawRect: method that draws a 10-pixel-wide red border around the view. Because UIKit drawing operations use Core Graphics for their underlying implementations, you can mix drawing calls, as shown here, to get the results you expect.

列表 3-5 显示了drawRect: 方法的一个简单实现, 它绘制了一个10个像素宽的红边框视图。 因为UIKit绘图操作使用内核图形(Core Graphics)来完成它们的后台实现, 你可以混合各种绘图调用,正如这里所示,来达到你期望的结果。

Listing 3-5  A drawing method

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGRect    myFrame = self.bounds;
 
    // Set the line width to 10 and inset the rectangle by
    // 5 pixels on all sides to compensate for the wider line.
    CGContextSetLineWidth(context, 10);
    CGRectInset(myFrame, 5, 5);
 
    [[UIColor redColor] set];
    UIRectFrame(myFrame);
}

If you know that your view’s drawing code always covers the entire surface of the view with opaque content, you can improve system performance by setting the opaque property of your view to YES. When you mark a view as opaque, UIKit avoids drawing content that is located immediately behind your view. This not only reduces the amount of time spent drawing but also minimizes the work that must be done to composite your view with other content. However, you should set this property to YES only if you know your view’s content is completely opaque. If your view cannot guarantee that its contents are always opaque, you should set the property to NO.

如果你知道你视图的绘图代码永远用不透明的内容覆盖整个视图表明,你就可以通过设置视图的 opaque 属性为YES来提高系统的性能。当你把视图标记为不透明时,UIKit避免绘制那些因此在视图后面的内容。这样做不仅减少了绘图的总时间,还减少了视图必须跟别的内容的合成工作。然而,只要你知道你的视图内容是完全不透明的,那么你就应该设置该属性为YES。 如果你的视图不能保证都是不透明的,你应该设置它为NO。

Another way to improve drawing performance, especially during scrolling, is to set theclearsContextBeforeDrawing property of your view to NO. When this property is set to YES, UIKIt automatically fills the area to be updated by your drawRect: method with transparent black before calling your method. Setting this property to NO eliminates the overhead for that fill operation but puts the burden on your application to fill the update rectangle passed to your drawRect: method with content.

另一个提供绘图性能的方法,特别是滚动期间,是设置视图的clearsContextBeforeDrawing 属性为NO。 当这个属性被设置为YES, UIKit自动通过drawRect: 方法在调用你的方法之前用透明的黑色背景填充整个区域。设置该属性为NO消除了填充操作的开销(overhead),但是把负担加到应用程序填充更新矩形,并把内容传递给drawRect: 方法。

Responding to Events

4、响应事件

View objects are responder objects—instances of the UIResponder class—and are therefore capable of receiving touch events. When a touch event occurs, the window dispatches the corresponding event object to the view in which the touch occurred. If your view is not interested in an event, it can ignore it or pass it up the responder chain to be handled by a different object.

视图对象都是响应者对象---都是UIResponder类的一个实例---因此能接收触摸事件。当一个触摸事件发生时,窗口发派相关的事件对象给触摸事件发生的视图。 如果你的视图对事件不感兴趣,它能忽视它或者把它传递到响应者链由别的对象来处理。

In addition to handling touch events directly, views can also use gesture recognizers to detect taps, swipes, pinches, and other types of common touch-related gestures. Gesture recognizers do the hard work of tracking touch events and making sure that they follow the right criteria to qualify them as the target gesture. Instead of your application having to track touch events, you can create the gesture recognizer, assign an appropriate target object and action method to it, and install it on your view using the addGestureRecognizer: method. The gesture recognizer then calls your action method when the corresponding gesture occurs.

除了直接处理触摸事件,视图还能使用手势识别来侦察点击(taps), 滑动(swipes), 捏合(piches), 以及其他类型的常用触摸相关的手势。 手势触摸完成追踪触摸事件的困难工作,并确保它们遵守正确的标准来识别出它们的目标手势。 应用程序不需要追踪触摸事件,你只需要创建手势识别(gesture recognizer), 分配一个适当的目标对象并对它操作方法, 然后使用addGestureRecognizer: 方法来把它载入你的视图。

If you prefer to handle touch events directly, you can implement the following methods for your view, which are described in more detail in Event Handling Guide for iOS:

如果你更喜欢直接处理触摸事件,你可以在视图里实现以下方法,它们在Event Handling Guide for iOS 里有着更详细的描述:

The default behavior for views is to respond to only one touch at a time. If the user puts a second finger down, the system ignores the touch event and does not report it to your view. If you plan to track multifinger gestures from your view’s event-handler methods, you need to enable multitouch events by setting the multipleTouchEnabled propertyof your view to YES.

视图的默认行为是一次只响应一个触摸事件。 如果用户按下第二个手指,系统忽视该触摸事件,并不向视图报告。 如果你计划从视图的事件处理器方法中追踪多手指手势,你需要设置视图的multipleTouchEnabled 属性来启动多触摸事件。

Some views, such as labels and images, disable event handling altogether initially. You can control whether a view is able to receive touch events by changing the value of the view’s userInteractionEnabled property. You might temporarily set this property to NO to prevent the user from manipulating the contents of your view while a long operation is pending. To prevent events from reaching any of your views, you can also use thebeginIgnoringInteractionEvents and endIgnoringInteractionEvents methods of the UIApplication object. These methods affect the delivery of events for the entire application, not just for a single view.

一些视图,比如标签和图片,最初完全禁用事件处理。你可以通过改变视图的userInteractionEnabled 属性值来控制视图是否允许接收触摸事件。当一个长事件操作待处理时,你可能临时设置该属性为NO来阻止用户操作视图的内容。为了阻止事件到达任何一个视图,你还可以使用UIApplication 对象的beginIgnoringInteractionEvents  和 endIgnoringInteractionEvents 方法。 这些方法影响整个应用程序事件的传递,不仅仅是影响单个视图。

Note: The animation methods of UIView typically disable touch events while animations are in progress. You can override this behavior by configuring the animation appropriately. For more information about performing animations, see “Animations.”

 注意:当动画在进行时,UIView 的动画方法通常不启动触摸事件。你可以通过适当地配置动画来重载该行为。 关于执行动画的更多信息,请看“Animations.”

As it handles touch events, UIKit uses the hitTest:withEvent: and pointInside:withEvent: methods of UIViewto determine whether a touch event occurred inside a given view’s bounds. Although you rarely need to override these methods, you could do so to implement custom touch behaviors for your view. For example, you could override these methods to prevent subviews from handling touch events.

当它处理触摸事件时,UIKit 使用UIView 的 hitTest:withEvent: 和 pointInside:withEvent: 方法来决定一个触摸事件是否发生在一个给定视图的边界内。 尽管很少需要重载这些方法,你可以在你的视图里重载它们来实现自定义触摸行为。 比如,你可以重载这些方法来阻止子视图处理触摸事件。

Cleaning Up After Your View

5、继你的视图之后清除

If your view class allocates any memory, stores references to any custom objects, or holds resources that must bereleased when the view is released, you must implement a dealloc method. The system calls the dealloc method when your view’s retain count reaches zero and it is time to deallocate the view. Your implementation of this method should release any objects or resources held by the view and then call the inherited implementation, as shown inListing 3-6. You should not use this method to perform any other types of tasks.

如果你的视图类分配了任何内存,存储了指向任何自定义对象的引用,或保留了一些必须在视图被释放之后被释放的资源, 你必须实现一个dealloc 方法。系统在你的视图的保留计数值(retain count)变为0时调用dealloc方法,由该方法来释放视图。该方法的实现应该释放由视图保留的任何对象和资源,然后调用被继承的实现,正如列表3-6所示。 你不应该使用该方法来执行任何其它类型的任务。

Listing 3-6  Implementing the dealloc method

- (void)dealloc {
    // Release a retained UIColor object
    [color release];
 
    // Call the inherited implementation
    [super dealloc];
}

 

posted on 2013-07-09 11:42  cainiaozhang  阅读(1372)  评论(1编辑  收藏  举报