【IOS笔记】Views

Views

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

通过view对象是与用户交互的主要方式,它们有很多责任,下面是其中一些:

  • 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.改写子视图的位置和大小

    • 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.   接收touch事件

    • 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.

Creating View Objects Using Interface Builder  通过IB创建view对象

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 anib file, which is a resource file that preserves the state and configuration of your objects.

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.

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 NSBundle or UINib object, which use the data in the nib file to reconstitute your view objects.

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 Creating Custom Content 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.

Creating View Objects Programmatically   编程方式创建

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:

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 method that you should use instead. For information about any custom initialization methods, see the reference documentation for the class.

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.

Setting the Properties of a View   设置View的属性

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.

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.

Table 3-1  Usage of some key view properties

Properties

Usage

alphahiddenopaque

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

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 YESimproves performance by eliminating unnecessary compositing operations.

boundsframecenter,transform

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

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.

For information about the relationship between the boundsframe, and center properties, see The Relationship of the Frame, Bounds, and Center Properties. For information about how transforms affect a view, see 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.

contentMode,contentStretch,contentScaleFactor

These properties affect the rendering behavior of content inside the view. The contentMode and contentStretchproperties 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.

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 Views in Drawing and Printing Guide for iOS.

gestureRecognizers,userInteractionEnabled,multipleTouchEnabled,exclusiveTouch

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

For information about how to respond to events in your views, see 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. The subviews 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.

For more advanced content, you can work directly with the view’s Core Animation layer. To specify an entirely different type of layer for the view, you must override the layerClass method.

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.

Tagging Views for Future Identification  通过加标记来帮助识别

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.

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.

基于tag的搜索, viewWithTag方法,改方法执行深度优先的搜索,但不会搜索父视图。

Creating and Managing a View Hierarchy  创建和管理view层次

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.

管理视图层次是app用户界面部分非常重要的一部分工作。视图的组织影响外观和事件处理。例如,视图的父子关系决定了哪个对象会先响应touch事件。相似的,父子关系定义了视图如何响应界面方向的改变。

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显示了分层的view如何构成了一个可视化应用界面。

Figure 3-1  Layered views in the Clock applicationLayered 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应用中建立视图层次,包括图形化的ib和编码方式。下面的小节介绍了如何组织视图层次,如果在视图层次中找到视图,以及坐标变换。

Adding and Removing Subviews   添加和移除自视图

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.

IB是最创建视图层次最方便的方式。当使用ib的时候,你将视图层次保存在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方法给父添加子视图。这个方法将子视图添加到父视图subviews列表的末端。

  • 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:, orexchangeSubviewAtIndex: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.

当添加子视图到父视图,子视图的当前frame矩形代表了在父视图内的初始位置。子视图超出父视图的部分默认不会被裁减。如果你希望子视图被裁减以不超过父视图的边界,你必须显式设置父视图的clipsToBounds 属性为YES。

The most common example of adding a subview to another view occurs in the application: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显示了此方法的一个版本,安装view到应用程序的window中。window和视图控制器都存储在应用程序的nib文件中。

Listing 3-1  Adding a view to a window

 

- (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 viewDidLoad methods of a view controller. If you are building your views programmatically, you put your view creation code in the loadView 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中。无论编程或ib方式创建的视图,你都可以在viewDidLoad代码中添加额外的视图配置代码。

Listing 3-2 shows the viewDidLoad method of the TransitionsViewController class from the UICatalog: Creating and Customizing UIKit Controls (Obj-C and Swift) 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显示了viewDidLoad方法。。。。

Listing 3-2  Adding views to an existing view hierarchy

- (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.

重要:父视图自动维护它们的子视图,因此在嵌入一个子视图后,释放子视图也是安全的。实际上,这是推荐做法,因为它阻止了你的应用程序中同一时间维护太多视图,容易引起内存泄漏。仅仅需要记住,如果你将视图从父视图移除,并且想以后重用,则你必须保留子视图的引用。removeFromSuperview方法中将子视图从父视图移除前会自动释放之,如果你不想在下次使用前持有视图引用,这个视图会自动释放。

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

关于内存管理,参见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 the willMoveToSuperview:willMoveToWindow:willRemoveSubview:didAddSubview:,didMoveToSuperview, or didMoveToWindow methods. You can use these notifications to update any state information related to your view hierarchy or to perform additional tasks.

当你添加一个子视图到另一个视图,UIKit通知父视图和子视图这个改变。如果你实现自定义视图,你可以通过覆盖一个或多个方法来拦截这些通知,这些方法包括:willMoveToSuperview:willMoveToWindow:willRemoveSubview:didAddSubview:,didMoveToSuperview, or 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 superviewproperty is set to nil. For views that are currently onscreen, the window object is the root view of the view hierarchy.

在创建一个视图层次后,你可以通过superView或subViews方法来导航。view的window属性包含了view当前所在的window。因为root view没有父视图,因此它的superview属性为nil。对于当前中屏幕上显示的视图,就是window对象的root view属性;

Hiding Views  隐藏视图

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 。隐藏的视图不会从系统接收到touch事件。然而,隐藏的视图会参与视图层次中的布局和大小调整。因此,隐藏视图的一个常常可替换的做法是从视图层次中移除视图,特别是你计划稍后会再显示这个视图。

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.

重要:如果你隐藏一个作为first responder的视图,这个视图不会自动放弃它的first responder状态。事件仍然会被分发到这个隐藏的视图。要阻止这个现象发生,你需要强制你的视图放弃first responder状态。详见: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’s alpha property. The hidden property is not an animatable property, so any changes you make to it take effect immediately.

如果你想为视图从可见到隐藏的变化过程添加动画效果,你必须使用alpha属性。hidden属性不是动画属性,因此,修改hidden属性会立刻生效。

Locating Views in a View Hierarchy   在视图层次中定位一个视图

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.

有两种方法在视图层次中定位视图:

  • 通过视图的指针,view controller 就是这样做的;
  • 为每个view赋予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.

保存视图的引用是最常有的方法,做使用时非常方便。如果你使用ib创建视图,你可以通过outlet连接到对象。对于编程方式创建的视图,你可以将视图引用保存在私有成员变量中。如果你使用outlet或者私有成员变量,你要负责释放它们。保证对象获取和释放正确的方式是使用声明属性。

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.

标记是一个减少硬编码和支持更加动态和灵活方式定位view的解决方案。可以通过tag定位view,tag也是引用view的更具有持久性的方式。

Translating, Scaling, and Rotating Views   转换,缩放和旋转

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.

每个视图都有一个映射变换属性可用来转换,缩放或者旋转视图内容。视图转换改变内容的外观显示,并且常常用来实现滚动,动画,或其它可视效果。

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,这个值不会改变视图外观。你可以在任何时候给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.

在上面的代码中,对一个视图应用变换,会对视图进行基于中心点的时钟旋转。图3-2演示了这个效果。

Figure 3-2  Rotating a view 45 degrees

When applying multiple transformations to a view, the order in which you add those transformations to the CGAffineTransform 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.

给一个视图应用多重变换时,你添加到CGAffineTransform 结构中的变换顺序非常重要。旋转视图然后转换与先转换然后旋转是不同的。即使转换和旋转的数量是一样的,但是不同的顺序会产生不同的结果。另外,任何转换都是基于中心点的。因此,应用旋转因子会围着中心点来旋转视图,缩放视图会改变大小,但是不会改变中心点。

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

Converting Coordinates in the View Hierarchy    转换坐标系

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:

许多时候,尤其是在事件处理时,一个应用程序会需要转换坐标值。例如,touch事件以window的坐标系报告位置,但是视图对象常常需要基于视图自己的坐标系。UIView类定义类下列方法来to或from local 坐标的转换坐标系:

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.

fromView方法从其他view的坐标系转换到当前视图的本地坐标系。相反地,toView方法从当前视图的坐标系转换到其它视图的坐标系。如果在使用这些方法时,你指定nil作为view的引用,转换会在当前视图坐标系,和包含当前视图的window的坐标系间发生。

In addition to the UIView conversion methods, the UIWindow class also defines several conversion methods. These methods are similar to the UIViewversions 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.

除此之外,UIWindow还定义了几个转换方法。这些方法用于在view和window坐标系间转换。

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演示了旋转会如何影响矩形大小的改变。在图中,一个外部父视图包含一个旋转的子视图。在子视图的坐标系中转换矩形导致矩形变大。这个变大的矩形实际是完全封闭旋转矩形的最小矩形。

Figure 3-3  Converting values in a rotated view

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   为布局改变做准备

Layout changes can occur whenever any of the following events happens in a view:布局发生改变的场合:

  • The size of a view’s bounds rectangle changes.view的bounds矩形的大小发生改变;

  • An interface orientation change occurs, which usually triggers a change in the root view’s bounds rectangle.接口方向发生改变,这常常是引起root view的bounds矩形发生改变;

  • The set of Core Animation sublayers associated with the view’s layer changes and requires layout.一组与视图layer相关的Core Animation子层发生改变,并要求更新布局;

  • Your application forces layout to occur by calling the setNeedsLayout or layoutIfNeeded method of a view.你的应用程序强制布局更新发生,by 调用view的 setNeedsLayout or layoutIfNeeded 方法;

  • Your application forces layout by calling the setNeedsLayout method of the view’s underlying layer object.你的app强制布局更新by调用view对象的layer对象的setNeedsLayout 方法。

Handling Layout Changes Automatically Using Autoresizing Rules

布局自动适应

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 theautoresizingMask 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属性对于其自动布局是非常重要的。表3-2列出了自动布局的选项。你可以使用or操作来组合常量值,然后赋给autoresizingMask 属性。如果你使用IB来组合你的视图,可使用属性编辑器设置。

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示意了图示了自动化布局属性的含义。当父视图边界调整时,指定了属性的一边会自动变化。没有指定属性的边具有固定的大小。如果你在一个方向上指定了超过一个布局规则,那么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.

使用IB是最简单的配置自动布局的方式。上图中宽、高的自动调整常量与IB界面中宽、大小指示具有相同效果。然而,IB中的边距指示器具有明显相反的效果。在IB中,显示的边距指示器意味着这个边是固定的,没有指定的那一边才会自动调整。幸运的是,IB提供了动画来说明应用效果。

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.

重要:如果view的 transform属性值不是identity transform,则view的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  布局的手工微调

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 layoutSubviews method 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.添加或删除子视图,或者Core Animation层

  • Force a subview to be redrawn by calling its setNeedsDisplay or setNeedsDisplayInRect: method.调用 setNeedsDisplay or 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.

实现一个大的滚动区域,是一个常常需要手工布局子视图的例子。因为用单独一个大的视图来做滚动内容是不合适的,app进程会用一个包含了很多小视图的root view来达到目的。每一段代表滚动内容的一部分。当一个滚动事件发生的时候,root view调用它的 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 ScrollViewSuite sample.

关于自动布局如何影响视图的大小和位置,参考:Handling Layout Changes Automatically Using Autoresizing Rules

关于滚动内容分段,参考: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:

应用程序接收用户的输入,根据用户输入调整视图来做出响应。一个app会修改它的视图-排列、改变大小和位置,隐藏或显示,加载一组新视图。在ios app中,有几种方式来执行这些操作:

  • In a view controller:  在view控制器中

    • 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.当实现特定效果,你可以使用一个动画块来改变视图的各种属性。例如,动画改变视图大小,你应该修改frame矩形的大小。

  • 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.当touch或手势事件发生,你的界面可能会加载一组新的视图来响应。

    • 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交互时,一个较大的滚动区域会隐藏,同时显示tile的子视图。详情参见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 Programming Guide for iOS.当键盘显示,你可以改变视图的位置和大小,避免视图被键盘盖住。关于键盘处理的详情,参见Text 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 the UIView class makes it easy to animate changes to view properties. You can also use the transitionWithView: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: ortransitionFromView: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

Interacting with Core Animation Layers  Core Animation层交互

 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.

每个视图对象有个专门用于动画显示的Core Animation层。尽管你对view对象可以做许多操作,你也需要直接操作Core Animation层。这就是view的layer属性。

Changing the Layer Class Associated with a View   改变view的layer类

The type of layer associated with a view cannot be changed after the view is created. Therefore, each view uses the layerClass 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 can change this value to use a different kind of layer. For example, if your view uses tiling to display a large scrollable area, you might want to use the CATiledLayer class to back your view.

view创建之后,它的layer类型不能改变。因此每个视图都是使用layerClass类方法来改变layer对象。这个方法的缺省实现是返回 CALayer,修改这个值的唯一方法是实现一个子类,override这个方法,返回不同的值。你可以改变这个值来使用不同的层。例如,如果你的视图显示一个tiling的很大的滚动区域,你会希望为这个view使用CATiledLayer 类。

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

实现layerClass方法只需简单创建想要的类,然后返回它。

+ (Class)layerClass
{
    return [CATiledLayer 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方法,然后用返回的类创建它的layer对象。实际上,视图总是将自己作为layer对象的delegate。就此而言,view拥有的layer以及view与layer的关系不能改变。你决不能将同一个view做为其他layer对象的delegate。改变这个拥有关系,或者delegate关系,会引起绘图问题以及潜在的程序崩溃可能。

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

关于Core Animation提供的各种layer对象,请参考Core Animation Reference Collection.

 

Embedding Layer Objects in a View   视图中嵌入的layer对象

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.

如果你喜欢首先操作layer对象而不是view,你可以根据需要将自定义layer对象整合进view层次。一个自定义layer对象是一个不为view所拥有的CALayer 实例。通常你用编码方式创建layer对象,使用Core Animation中的方法将它们整合起来。自定义layer对象不会接受事件或参与响应链,但会绘制它们自身,并且layer会根据父视图的改变而做出大小的改变,并遵循Core Animation规则。

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展示了一个view controller中viewDidLoad方法的示例,它创建了一个自定义layer对象,并将其加入root view。这个layer对象被用来展示静态图像。在这里没有将layer对象添加给view自己,你将其添加给了view的layer对象的下层(view.layer.addSublayer.

Listing 3-3  Adding a custom layer to a view

- (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.
如果你愿意,你可以添加无数多的子层,并将它们组织到子层次。然而,这些层必须赋给一个view的layer对象。

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

关于如何操作layer,参考: 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.

如果标准系统view不完全满足你的需要,可以自定义view。自定义view让你可以完全操控view的显示和交互能力。

Note: If you are using OpenGL ES to do your drawing, you should use the GLKView class instead of subclassing UIView. For more information about how to draw using OpenGL ES, see OpenGL ES Programming Guide for iOS.

注意:如果你正在使用OpenGL ES绘图,你应该使用GLKView类,而不是UIView。 

Checklist for Implementing a Custom View  实现自定义view的检查表

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 method or define a custom initialization method.覆盖initWithFrame:方法来自定义初始化

    • For views you plan to load from nib files, override the method. Use this method to initialize your view and put it into a known state.为要从nib文件加载的view,覆盖initWithCoder:方法,使用这个方法完成初始化,并将view置于一个已知的状态

  • Implement a 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 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.在view的初始化序列中创建子视图

    • Set the property of each subview at creation time.在创建时,设置每个子视图的autoresizingMask属性

    • If your subviews require custom layout, override the method and implement your layout code there.如果子视图需要自定义布局,覆盖layoutSubviews方法

  • To handle touch-based events, do the following:要处理基于touch的事件,需要:

  • If you want the printed version of your view to look different from the onscreen version, implement the: method. For detailed information about how to support printing in your views, see.如果你希望视图的打印版本看起来与屏幕显示版本不同,则要实现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, thecontentMode 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.

除了覆盖方法之外,记住你还可以针对view现有的属性和方法做很多事。例如,contentMode和contentStretch属性让你可以最终改变视图的渲染效果。除了UIView类自身之外,还有许多方面可通过直接或间接的配置CALayer对象来实现。你甚至可修改layer对象。

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

Initializing Your Custom View    初始化自定义视图

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初始化方法。这个方法负责在创建时初始化类并将视图对象置于一个已知状态。在编程方式创建view实例的时候使用这个方法。

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显示了一个initWithFram方法的标准实现骨架。这个方法先调用基类的实现,然后在返回对象前初始化对象的变量和状态。首先调用基类的实现是一个传统的实现方式,如果发生问题,你可以取消你的操作并返回nil。

Listing 3-4  Initializing a view subclass

 

- (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 theinitWithFrame: method to instantiate new view objects. Instead, it uses the initWithCoder: method that is part of the NSCoding protocol.

如果你计划从nib文件加载视图,你需要注意,中ios中nib加载的代码不是用initWithFram方法,用的是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 the awakeFromNib method in your view class and use that method to perform additional initialization.

尽管你的视图使用NSCoding协议,ib也不知道你的view的自定义属性,因此不会将自定义属性编码到nib文件。因此你的initWithCoder方法就需要做这些事,以保证view对象的状态正常。你还可以实现awakeFromeNib方法来执行其它初始化操作。

Implementing Your Drawing Code   实现自定义绘制

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.

实现你的绘制方法不仅仅是绘制内容这一件事。这个方法里不应该做更新app数据等任何与绘制无关的事。它应该配置绘图环境,绘制内容并且尽可能快的退出。如果你的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中每次调用这个方法时都会创建一个不同的context,因此你不要为了将来使用而试图保存context。

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立即拒绝绘制你的view下面的内容。这样不仅减少花费的时间,而且最小化将你的视图与其它内容组合的时间。然而,你应该在明确知道你的view是完全不透明的时候才这样做。如果你不能保证你的内容总是不透明,你应该讲这个属性设置为NO。

Another way to improve drawing performance, especially during scrolling, is to set the clearsContextBeforeDrawing 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会在调用你的绘图方法前自动使用透明黑色填充绘图区域。设置此属性为NO会完全节省填充操作的开销,但是你的drawRect方法则需要负责更新传给你的绘图区域。

Responding to Events  响应事件

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.

视图对象是responder对象--UIResponder类的实例,因此可以接受touch事件。当一个touch事件发生时,window会分发事件对象到那个视图。如果你的视图对一个事件不感兴趣,它可以忽略这个事件或者沿着响应链向上传递事件给其它对象处理。

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.

直接处理touch事件之外,视图也可以使用手势识别器来检测taps,swipes,pinches,以及其它类型的触摸手势。手势识别器为手势识别做了艰巨的工作,它跟踪touh事件并判断touch操作是否符合目标手势的判断标准。你无需通过touch事件来跟踪手势,你可以创建手势识别器,赋值给相应的目标对象,并添加处理方法,使用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:

如果你喜欢直接处理touch事件,你可以为视图实现下面的方法:

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 multi-finger gestures from your view’s event-handler methods, you need to enable multitouch events by setting the multipleTouchEnabled property of your view to YES.

视图的缺省行为是一次只响应一个touch。如果用户放下了第二根手指,系统忽略touch事件并且不会报告给你的视图。如果你计划跟踪多个手指的手势,你需要通过将multipleTouchEnabled设置为YES来启用多点触控事件。

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.

有一些视图,例如label和image,在初始化的时候禁用了事件处理功能。你可以通过改变userInteractionEnabled属性来控制视图是否可以接受touch事件。当你要进行一个长时间的操作,为了阻止用户操作视图,你可以临时将此属性置为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.

注意:典型的,动画方法中执行动画的时候就会禁用touch事件。你可以通过配置动画属性来覆盖这个行为。

As it handles touch events, UIKit uses the hitTest:withEvent: and pointInside:withEvent: methods of UIView to 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.

当处理touch事件,UIKit使用UIView的hitTest和pointInside方法来决定一个touch事件是否在某个视图的边界内发生了。尽管你很少需要覆盖这两个方法,但是你可以借助它们实现自定义的touch行为。例如,你可以覆盖这些方法来阻止子视图处理touch事件。

Cleaning Up After Your View    视图之后的清理

If your view class allocates any memory, stores references to any custom objects, or holds resources that must be released 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 in Listing 3-6. You should not use this method to perform any other types of tasks.

如果你的视图分配了内存,存储了其它对象的引用,或持有了在view释放的时候也必须要释放的资源,你必须实现dealloc方法。当你的view当引用计数达到0,应该要释放view对象的时候,系统会自动调用你的dealloc方法。你的实现需要释放view持有的任何对象和资源,然后再调用父类的实现(父类的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];
}
原文地址:https://www.cnblogs.com/jimcheng/p/4166942.html