View Architecture and Geometry IPHONE APPS

Because views are focal objects in iPhone applications, it is important to understand a little about how views interact with other parts of the system. The standard view classes in UIKit provide a considerable amount of behavior to your application for free. They also provide well-defined integration points where you can customize that behavior and do what you need to do for your application.

The following sections explain the standard behavior of views and call out the places where you can integrate your custom code. For information about the integration points of specific classes, see the reference document for that class.

The View Interaction Model

Any time a user interacts with your user interface, or your own code programmatically changes something, a complex sequence of events takes place inside of UIKit to handle that interaction. At specific points during that sequence, UIKit calls out to your view classes and gives them a chance to respond on behalf of your application. Understanding these callout points is important to understanding where your views fit into the system. Figure shows the basic sequence of events that starts with the user touching the screen and ends with the graphics system updating the screen content in response. Programmatic events follow the same basic steps without the initial user interaction.

UIKit interactions with your view objects

UIKit interactions with your view objects

The following steps break the event sequence in Figure down even further and explain what happens at each stage and how your application might want to react in response.

  1. The user touches the screen.
  2. The hardware reports the touch event to the UIKit framework.
  3. The UIKit framework packages the touch into a UIEvent object and dispatches it to the appropriate view. (For a detailed explanation of how UIKit delivers events to your views,see “Event Delivery”
  4. The event-handling methods of your view might respond to the event by doing any of the following:
    • Adjust the properties (frame, bounds, alpha, and so on) of the view or its
    • Mark the view (or its subviews) as needing a change in its layout.
    • Mark the view (or its subviews) as needing to be redrawn.
    • Notify a controller about changes to some piece of data.
  5. Of course, it is up to the view to decide which of these things must be done and call the appropriate methods to do it.

  6. If a view is marked as requiring layout, UIKit calls the view’s layoutSubviews method.
  7. You can override this method in your custom views and use it to adjust the position and size of any subviews. For example, a view that provides a large scrollable area would need to use several subviews as “tiles” rather than create one large view,which is not likely to fit in memory anyway. In its implementation of this method, the view would hide any subviews that are now offscreen or reposition them and use them to draw newly exposed content. As part of this process, the view can also mark the new tiles as needing to be redrawn.

  8. If any part of the view is marked as needing to be redrawn, UIKit calls the view’s drawRect: method.
  9. UIKit calls this method for only those views that need it. Each view’s implementation of this method should redraw the specified area as quickly as possible. Each view should draw only its own contents and not the contents of any subviews. Views should not attempt to make any further changes to their properties or layout at this point.

  10. Any updated views are composited with the rest of visible content and sent to the graphics hardware for display.
  11. The graphics hardware transfers the rendered content to the screen.

Given the preceding set of steps, the primary integration points for your own custom views are as follows:

  1. These event-handling methods:
    • touchesBegan:withEvent:
    • touchesMoved:withEvent:
    • touchesEnded:withEvent:
    • touchesCancelled:withEvent:
  2. The layoutSubviews method
  3. The drawRect: method

These are the methods that most custom views implement to get the behavior they want; you may not need to override all of them. For example, if you are implementing a view whose size never changes, you might not need to override the layoutSubviews method. Similarly, if you are implementing a view that displays simple content, such as text and images, you can often avoid drawing altogether by simply embedding UI ImageView and UI Label objects as subviews.

It is also important to remember that these are the primary integration points but not the only ones. Several methods of the UIView class are designed to be override points for sub classers. You should look at the method descriptions in UIView Class Reference to see which methods might be appropriate for you to override in your custom implementations.

The View Rendering Architecture

Although you use views to represent content onscreen, the UI View class itself actually relies heavily on another object for much of its basic behavior. Each view object in UI Kit is backed by a Core Animation layer object, which is an instance of the CA Layer class. This layer class provides the fundamental support for the layout and rendering of a view’s contents and for compositing and animating that content. In contrast with Mac OS X (in which Core Animation support is optional) iPhone OS integrates Core Animation into the heart of the view rendering implementation. Although Core Animation has a central role,UI Kit streamlines the programming experience by providing a transparent layer on top of Core Animation. This transparent layer eliminates the need to access Core Animation layers directly most of the time, instead letting you access similar behaviors using the methods and properties of the UI View class. Where Core Animation becomes important, however, is when the UI View class does not provide everything you need. At that point, you can dive down into the Core Animation layers and do some pretty sophisticated rendering for your application.

The following sections provide an introduction to Core Animation and describe some of the features it provides to you for free through the UIView class.

Core Animation Basics

Core Animation takes advantage of hardware acceleration and an optimized architecture to implement fast rendering and real-time animations. The first time a view’s drawRect: method is called, the layer captures the results into a bitmap. Subsequent redraw calls use this cached bitmap whenever possible to avoid calling the drawRect: method, which can be expensive. This process allows Core Animation to optimize its compositing operations and deliver the desired performance.

Core Animation stores the layers associated with your view objects in a hierarchy referred to as the layertree. Like views, each layer in the layer tree has a single parent and can have any number of embedded sublayers. By default, objects in the layer tree are organized exactly like the views in your view hierarchy. You can add layers, however, without adding a corresponding view. You might do this to implement special visual effects for which a view is not required.

Layer objects are actually the driving force behind the rendering and layout system in iPhone OS, and most view properties are actually thin wrappers for properties on the underlying layer object. When you change the property of a layer in the layer tree (directly using the CALayer object), the changed value is reflected immediately in the layer object. If the change triggers a corresponding animation, however, that change may not be reflected onscreen immediately; instead, it must be animated onto the screen over time. To manage these sorts of animations, Core Animation maintains two additional sets of layer objects in what are referred to as the presentation tree and the render tree.

The presentation tree reflects the state of the layers as they are currently presented to the user. When you animate the changing of a layer value, the presentation layer reflects the old value until the animation commences. As the animation progresses, Core Animation updates the value in the presentation-tree layer based on the current frame of the animation. The render tree then works together with the presentation tree to render the changes on the screen. Because the render tree runs in a separate process or thread, the work does does not impact your application’s main run loop. While both the layer tree and the presentation tree are public, the render tree is a private API.

The placement of layer objects behind your views has many important implications for the performance of your drawing code. The upside to using layers is that most geometry changes to your views do not require redrawing. For example, changing the position and size of a view does not require the system to redraw the contents of a view; it can simply reuse the cached bitmap created by the layer. Animating this cached content is significantly more efficient than trying to redraw that content every time.

The downside to using layers is that the additional cached data can add memory pressure to your application. If your application creates too many views or creates very large views, you could run out of memory quickly. You should not be afraid to use views in your application, but do not create new view objects if you have existing views that can be reused. In other words, pursue approaches that minimize the number of views you keep in memory at the same time.

Changing the Layer of a View

Because views are required to have an associated layer object in iPhone OS, the UIView class creates this layer automatically at initialization time. You can access the layer that is created through the layer property of the view, but you cannot change the layer object after the view is created.

If you want a view to use a different type of layer, you must override the view’s layerClass class method and return the class object for the layer you want it to use. The most common reason to return a different layer class is to implement an OpenGL-based application. To use OpenGL drawing commands, the layer for the underlying view must be an instance of the CA EAGLLayer class. This type of layer interacts with the OpenGL rendering calls to present the desired content on the screen.

Animation Support

One of the benefits of having a layer object behind every view in iPhone OS is that you can animate content more easily. Remember that animation is not necessarily about creating visual eye candy. Animations provide the user with a context for any changes that occur in your application’s user interface. For example, when you use a transition to move from one screen to another, you are indicating to users that the screens are related. The system provides automatic support for many of the most commonly used animations, but you can also create animations for other parts of your interface.

Many properties of the UIView class are considered to be animatable. An animatable property is one for which there is semiautomatic support for animating from one value to another. You must still tell UIKit that you want to perform the animation, but Core Animation assumes full responsibility for running the animation once it has begun. Among the properties you can animate on a UIView object are the following:

Even though other view properties are not directly animatable, you can create explicit animations for some of them. Explicit animations require you to do more of the work in managing the animation and the rendered contents, but they still use the underlying Core Animation infrastructure to obtain good performance.

View Coordinate Systems

Coordinates in UIKit are based on a coordinate system whose origin is in the top-left corner and whose coordinate axes extend down and to the right from that point. Coordinate values are represented using floating-point numbers, which allow for precise layout and positioning of content and allow for resolution independence. Figure shows this coordinate system relative to the screen, but this coordinate system is also used by the UIWindow and UIView classes. This particular orientation was chosen to make it easier to lay out controls and content in user interfaces, even though it differs from the default coordinate systems in use by Quartz and Mac OS X.

View coordinate system

View coordinate system

As you write your interface code, be aware of the coordinate 0system currently in effect. Every window and view object maintains its own local coordinate system. All drawing in a view occurs relative to the view’s local coordinate system. The frame rectangle for each view, however, is specified using the coordinate system of its parent view, and coordinates delivered as part of an event object are specified relative to the coordinate system of the enclosing window. For convenience, the UIWindow and UIView classes each provide methods to convert back and forth between the coordinate systems of different objects.

Although the coordinate system used by Quartz does not use the top-left corner as the origin point, for many Quartz calls this is not a problem. Before invoking your view’s drawRect: method, UIKit automatically configures the drawing environment to use a top-left origin. Quartz calls made within this environment draw correctly in your view. The only time you need to consider these different coordinate systems is when you set up the drawing environment yourself using Quartz.

The Relationship of the Frame, Bounds, and Center

A view object tracks its size and location using its frame, bounds, and center properties. The frame property contains a rectangle, the frame rectangle, that specifies the view’s location and size relative to its parent view’s coordinate system. The bounds property contains a rectangle, the bounds rectangle, that defines the view’s position and size relative to its own local coordinate system. And although the origin of the bounds rectangle is typically set to (0,0), it need not be. The center property contains the center point of the frame rectangle.

You use the frame, bounds, and center properties for different purposes in your code. Because the bounds rectangle represents the view’s local coordinate system, you use it most often during drawing or event-handling code when you need to know where in your view something happened. The center point represents the known center point of your view and is always the best way to manipulate the position of your view. The frame rectangle is a convenience value that is computed using the bounds and center point and is valid only when the view’s transform is set to the identity transform.

Figure shows the relationship between the frame and bounds rectangles. The complete image on the right is drawn in the view starting at (0,0). Because the size of the bounds does not match the full size of the image, however, only part of the image outside the bounds rectangle is clipped automatically. When the view is composited with its parent view, the position of the view inside its parent is determined by the origin of the view’s frame rectangle, which in this case is(5, 5). As a result, the view’s contents appear shifted down and to the right from the parent view’s origin.

Relationship between a view'sframe and bounds

Relationship between a view'sframe and bounds

When there is no transform applied to the view, the location and size of the view are determined by these three interrelated properties. The frame property of a view is set when a view object is created programmatically using the initWithFrame: method. That method also initializes the bounds rectangle to originate at (0.0, 0.0) and have the same size as the view's frame. The center property is then set to the center point of the frame.

Although you can set the values of these properties independently, setting the value for one changes the others in the following ways:

  • When you set the frame property, the size of the bounds property is set to match the size of the frame property. The center property is also adjusted to match the center point of the new frame.
  • When you set the center property, the origin of the frame changes accordingly.
  • When you set the size of the bounds rectangle, the size of the frame rectangle changes to match.

You can change the bounds origin without changing the other two properties. When you do, the view displays the portion of the underlying image that you have identified. In Figure, the original bounds origin is set to (0.0, 0.0). In Figure, that origin is moved to (8.0, 24.0). As a result, a different portion of the underlying image is displayed by the view. Because the frame rectangle did not change, however, the new content is displayed in the same location inside the parent view as before.

Altering a view's bounds

Altering a view's bounds

Coordinate System Transformations

Although coordinate system transformations are commonly used in a view’s drawRect: method to facilitate drawing, in iPhone OS, you can also use them to implement visual effects for your view. For example, the UIView class includes a transform property that lets you apply different types of translation, scaling, and zooming effects to the entire view. By default, the value of this property is the identity transform, which causes no changes to the view. To add transformations, get the CG Affine Transform structure stored in this property, use the corresponding Core Graphics functions to apply the transformations, and then assign the modified transform structure back to the view’s transform property.

Translating a view shifts all subviews along with the drawing of the view's content. Because coordinate systems of subviews inherit and build on these alterations, scaling also affects the drawing of the subviews.

Content Modes and Scaling

When you change the bounds of a view or apply a scaling factor to the transform property of a view, the frame rectangle is changed by a commensurate amount. Depending on the content mode associated with the view, the view’s content may also be scaled or repositioned to account for the changes. The view’s contentMode property determines the effect that bounds changes and scaling operations have on the view. By default, the value of this property is set to UI View Content Mode Scale To Fill, which always causes the view’s contents to be scaled to fit the new frame size. For example, Figure shows what happens when the horizontal scaling factor of the view is doubled.

View scaled using the scale-to-fill content mode

View scaled using the scale-to-fill content mode

Scaling of your view’s content occurs because the first time a view is shown, its rendered contents are cached in the underlying layer. Rather than force the view to redraw itself every time its bounds change or a scaling factor is applied, UIKit uses the view’s content mode to determine how to display the cached content. Figure compares the results of changing the bounds of a view or applying a scaling factor to it using several different content modes.

Content mode comparisons

Content mode comparisons

Although applying a scaling factor always scales the view’s contents, there are content modes that do not scale the view’s contents when the bounds of the view change. Several UI View Content Mode constants (such as UI View Content Mode Top and UI View Content Mode Bottom Right) display the current content in different corners or along different edges of the view. There is also a mode for displaying the content centered inside the view. Changing the bounds rectangle with one of these content modes in place simply moves the existing contents to the appropriate location inside the new bounds rectangle.

Do consider using content modes when you want to implement resizable controls in your application; by doing so you can avoid both control distortion and the writing of custom drawing code. Buttons and segmented controls are particularly suitable for content mode–based drawing. They typically use several images to create the appearance of the control. In addition to having two fixed-size end cap images, a button that can grow horizontally uses a stretchable center image that is only 1 pixel wide. By displaying each image in its own image view and setting the content mode of the stretchable middle image to UI View Content Mode Scale To Fill, the button can grow in size without distorting the appearance of the end caps. More importantly, the images associated with each image view can be cached by Core Animation and animated without any custom drawing code, which results in much better performance.

Although content modes are good to avoid redrawing the contents of your view, you can also use the UI View Content Mode Redraw content mode when you specifically want control over the appearance of your view during scaling and resizing operations. Setting your view’s content mode to this value forces Core Animation to invalidate your view’s contents and call your view’s drawRect: method rather than scale or resize them automatically.

Autoresizing Behaviors

When you change the frame rectangle of a view, the position and size of embedded subviews often needs to change to match the new size of the original view. If the auto resizes Subviews property of a view is set to YES, its subviews are automatically resized according to the values in the autoresizing Mask property. Often, simply configuring the auto resizing mask for a view provides the appropriate behavior for an application. Otherwise, it is the application's responsibility to reposition and resize the subviews by overriding the layout Subviews method.

To set a view’s autoresizing behaviors, combine the desired autoresizing constants using a bitwise OR operator and assign the resulting value to the view’s autoresizing Mask property. The autoresizing constants and describes how each one affects the size and placement of a given view. For example, to keep a view pinned to the lower-left corner of its superview, add the UI View Autoresizing Flexible RightMargin and UI View Autoresizing Flexible TopMargin constants and assign them to the auto resizing Mask property. When more than one aspect along an axis is made flexible, the resize amount is distributed evenly among them.

Autoresizing mask constants

Autoresizing mask constants

Figure provides a graphical representation of the position of the constant values. When one of these constants is omitted, the view's layout is fixed in that aspect; when a constant is included in the mask, the view's layout is flexible in that aspect.

View autoresizing mask constants

View autoresizing mask constants

If you are using Interface Builder to configure your views, you can set the autoresizing behavior for each view by using the Autosizing controls in the Size inspector. Although the flexible width and height constants from the preceding figure have the same behavior as the Interface Builder springs located in the same position have, the behavior of the margin constants is effectively reversed. In other words, to apply the flexible right margin autoresizing behavior to a view in Interface Builder, you must leave the space on that side of the Autosizing control empty, not place a strut there. Fortunately, Interface Builder provides an animation to show you how changes to the autoresizing behaviors affect your view.

If the autoresizesSubviews property of a view is set to NO, any autoresizing behaviors set on the immediate subviews of that view are ignored. Similarly, if a subview’s autoresizing mask is set to UI View Autoresizing None, the subview does not change size and so its immediate subviews are never resized either.

The behavior is undefined if it is not. Although autoresizing behaviors may be suitable for some layout needs, if you want more control over the layout of your views, you should override the layoutSubviews method in the appropriate view classes. For more information about managing the layout of your views, see “Responding to Layout Changes”.

All rights reserved © 2018 Wisdom IT Services India Pvt. Ltd Protection Status