Important: This document contains information that used to be in iOS Application Programming Guide. The information in this document has not been updated specifically for iOS 4.0.
High-quality graphics are an important part of your application’s user interface. Providing high-quality graphics not only makes your application look good, but it also makes your application look like a natural extension to the rest of the system. iOS provides two primary paths for creating high-quality graphics in your system: OpenGL or native rendering using Quartz, Core Animation, and UIKit.
The OpenGL frameworks are geared primarily toward game development or applications that require high frame rates. OpenGL is a C-based interface used to create 2D and 3D content on desktop computers. iOS supports OpenGL drawing through the OpenGL ES framework, which provides support for both the OpenGL ES 2.0 and OpenGL ES v1.1 specifications. OpenGL ES is designed specifically for use on embedded hardware systems and differs in many ways from desktop versions of OpenGL.
For developers who want a more object-oriented drawing approach, iOS provides Quartz, Core Animation, and the graphics support in UIKit. Quartz is the main drawing interface, providing support for path-based drawing, anti-aliased rendering, gradient fill patterns, images, colors, coordinate-space transformations, and PDF document creation, display, and parsing. UIKit provides Objective-C wrappers for Quartz images and color manipulations. Core Animation provides the underlying support for animating changes in many UIKit view properties and can also be used to implement custom animations.
This chapter provides an overview of the drawing process for iPhone applications, along with specific drawing techniques for each of the supported drawing technologies. This chapter also provides tips and guidance on how to optimize your drawing code for the iOS platform.
Important: The UIKit classes are generally not thread safe. All drawing-related operations should be performed on your application’s main thread.
In iOS, all drawing—regardless of whether it involves OpenGL, Quartz, UIKit, or Core Animation—occurs within the confines of a UIView
object. Views define the portion of the screen in which drawing occurs. If you use system-provided views, this drawing is handled for you automatically. If you define custom views, however, you must provide the drawing code yourself. For applications that draw using OpenGL, once you set up your rendering surface, you use the drawing model specified by OpenGL.
For Quartz, Core Animation, and UIKit, you use the drawing concepts described in the following sections.
The basic drawing model for UIView
objects involves updating content on demand. The UIView
class makes the update process easier and more efficient, however, by gathering the update requests you make and delivering them to your drawing code at the most appropriate time.
Whenever a portion of your view needs to be redrawn, the UIView
object’s built-in drawing code calls its drawRect:
method. It passes this method a rectangle indicating the portion of your view that needs to be redrawn. You override this method in your custom view subclasses and use it to draw the contents of your view. The first time your view is drawn, the rectangle passed to the drawRect:
method contains your view’s entire visible area. During subsequent calls, however, this rectangle represents only the portion of the view that actually needs to be redrawn. There are several actions that can trigger a view update:
Moving or removing another view that was partially obscuring your view
Making a previously hidden view visible again by setting its hidden
property to NO
Scrolling a view off the screen and then back on
Explicitly calling the setNeedsDisplay
or setNeedsDisplayInRect:
method of your view
After calling your drawRect:
method, the view marks itself as updated and waits for new actions to arrive and trigger another update cycle. If your view displays static content, then all you need to do is respond to changes in your view’s visibility caused by scrolling and the presence of other views. If you update your view’s content periodically, however, you must determine when to call the setNeedsDisplay
or setNeedsDisplayInRect:
method to trigger an update. For example, if you were updating content several times a second, you might want to set up a timer to update your view. You might also update your view in response to user interactions or the creation of new content in your view.
As described in “View Coordinate Systems,” the origin of a window or view is located in its top-left corner, and positive coordinate values extend down and to the right of this origin. When you write your drawing code, you use this coordinate system to specify the location of individual points for the content you draw.
If you need to make changes to the default coordinate system, you do so by modifying the current transformation matrix. The current transformation matrix (CTM) is a mathematical matrix that maps points in your view’s coordinate system to points on the device’s screen. When your view’s drawRect:
method is first called, the CTM is configured so that the origin of the coordinate system matches the your view’s origin and its positive axes extend down and to the right. However, you can change the CTM by adding scaling, rotation, and translation factors to it and thereby change the size, orientation, and position of the default coordinate system relative to the underlying view or window.
Modifying the CTM is the standard technique used to draw content in your view because it involves much less work. If you want to draw a 10 x 10 square starting at the point (20, 20) in the current drawing system, you could create a path that moves to (20, 20) and then draws the needed set of lines to complete the square. If you decide later that you want to move that square to the point (10, 10), however, you would have to recreate the path with the new starting point. In fact, you would have to recreate the path every time you changed the origin. Creating paths is a relatively expensive operation, but creating a square whose origin is at (0, 0) and modifying the CTM to match the desired drawing origin is cheap by comparison.
In the Core Graphics framework, there are two ways to modify the CTM. You can modify the CTM directly using the CTM manipulation functions defined in CGContext Reference. You can also create a CGAffineTransform
structure, apply any transformations you want, and then concatenate that transform onto the CTM. Using an affine transform lets you group transformations and then apply them to the CTM all at once. You can also evaluate and invert affine transforms and use them to modify point, size, and rectangle values in your code. For more information on using affine transforms, see Quartz 2D Programming Guide and CGAffineTransform Reference.
Before calling your custom drawRect:
method, the view object automatically configures its drawing environment so that your code can start drawing immediately. As part of this configuration, the UIView
object creates a graphics context (a CGContextRef
opaque type) for the current drawing environment. This graphics context contains the information the drawing system needs to perform any subsequent drawing commands. It defines basic drawing attributes such as the colors to use when drawing, the clipping area, line width and style information, font information, compositing options, and several others.
You can create custom graphics context objects in situations where you want to draw somewhere other than your view. In Quartz, you primarily do this when you want to capture a series of drawing commands and use them to create an image or a PDF file. To create the context, you use the CGBitmapContextCreate
or CGPDFContextCreate
function. Once you have the context, you can pass it to the drawing functions needed to create your content.
When creating custom contexts, the coordinate system for those contexts is different than the native coordinate system used by iOS. Instead of the origin being in the upper-left corner of the drawing surface, it is in the lower-left corner and the axes point up and to the right. The coordinates you specify in your drawing commands must take this into consideration or the resulting image or PDF file may appear wrong when rendered.
Important: Because you use a lower-left origin when drawing into a bitmap or PDF context, you must compensate for that coordinate system when rendering the resulting content into a view. In other words, if you create an image and draw it using the CGContextDrawImage
function, the image will appear upside down by default. To correct for this, you must invert the y axis of the CTM (by multiplying it by -1
) and shift the origin from the lower-left corner to the upper-left corner of the view.
If you use a UIImage
object to wrap a CGImageRef
you create, you do not need to modify the CTM. The UIImage
object automatically compensates for the inverted coordinate system of the CGImageRef
type.
For more information about graphics contexts, modifying the graphics state information, and using graphics contexts to create custom content, see Quartz 2D Programming Guide. For a list of functions used in conjunction with graphics contexts, see CGContext Reference, CGBitmapContext Reference, and CGPDFContext Reference.
The Quartz drawing system uses a vector-based drawing model. Compared to a raster-based drawing model, in which drawing commands operate on individual pixels, drawing commands in Quartz are specified using a fixed-scale drawing space, known as the user coordinate space. iOS then maps the coordinates in this drawing space onto the actual pixels of the device. The advantage of this model is that graphics drawn using vector commands continue to look good when scaled up or down using an affine transform.
In order to maintain the precision inherent in a vector-based drawing system, drawing coordinates are specified using floating-point values instead of integers. The use of floating-point values for coordinates makes it possible for you to specify the location of your program's content very precisely. For the most part, you do not have to worry about how those values are eventually mapped to the device’s screen.
The user coordinate space is the environment that you use for all of your drawing commands. The units of this space are measured in points. The device coordinate space refers to the native coordinate space of the device, which is measured in pixels. By default, one point in user coordinate space is equal to one pixel in device space, which results in 1 point equaling 1/160th of an inch. You should not assume that this 1-to-1 ratio will always be the case, however.
iOS supports the full range of color spaces available in Quartz; however, most applications should need only the RGB color space. Because iOS is designed to run on embedded hardware and display graphics on a screen, the RGB color space is the most appropriate one to use.
The UIColor
object provides convenience methods for specifying color values using RGB, HSB, and grayscale values. When creating colors in this way, you never need to specify the color space. It is determined for you automatically by the UIColor
object.
You can also use the CGContextSetRGBStrokeColor
and CGContextSetRGBFillColor
functions in the Core Graphics framework to create and set colors. Although the Core Graphics framework includes support for creating colors using other color spaces, and for creating custom color spaces, using those colors in your drawing code is not recommended. Your drawing code should always use RGB colors.
Table 2-1 lists the image formats supported directly by iOS. Of these formats, the PNG format is the one most recommended for use in your applications.
Format | Filename extensions |
---|---|
Portable Network Graphic (PNG) |
|
Tagged Image File Format (TIFF) |
|
Joint Photographic Experts Group (JPEG) |
|
Graphic Interchange Format (GIF) |
|
Windows Bitmap Format (DIB) |
|
Windows Icon Format |
|
Windows Cursor |
|
XWindow bitmap |
|
The following sections provide tips on how to write quality drawing code while ensuring that your application looks appealing to end users.
Depending on the type of application you are creating, it may be possible to use little or no custom drawing code. Although immersive applications typically make extensive use of custom drawing code, utility and productivity applications can often use standard views and controls to display their content.
The use of custom drawing code should be limited to situations where the content you display needs to change dynamically. For example, a drawing application would need to use custom drawing code to track the user’s drawing commands and a game would be updating the screen constantly to reflect the changing game environment. In those situations, you would need to choose an appropriate drawing technology and create a custom view class to handle events and update the display appropriately.
On the other hand, if the bulk of your application’s interface is fixed, you can render the interface in advance to one or more image files and display those images at runtime using UIImageView
objects. You can layer image views with other content as needed to build your interface. For example, you could use UILabel
objects to display configurable text and include buttons or other controls to provide interactivity.
Drawing is a relatively expensive operation on any platform, and optimizing your drawing code should always be an important step in your development process. Table 2-2 lists several tips for ensuring that your drawing code is as optimal as possible. In addition to these tips, you should always use the available performance tools to test your code and remove hotspots and redundancies.
Tip | Action |
---|---|
Draw minimally | During each update cycle, you should update only the portions of your view that actually changed. If you are using the |
Mark opaque views as such | Compositing a view whose contents are opaque requires much less effort than compositing one that is partially transparent. To make a view opaque, the contents of the view must not contain any transparency and the |
Remove alpha channels from opaque PNG files | If every pixel of a PNG image is opaque, removing the alpha channel avoids the need to blend the layers containing that image. This simplifies compositing of the image considerably and improves drawing performance. |
Reuse table cells and views during scrolling | Creating new views during scrolling should be avoided at all costs. Taking the time to create new views reduces the amount of time available for updating the screen, which leads to uneven scrolling behavior. |
Avoid clearing the previous content during scrolling | By default, UIKit clears a view’s current context buffer prior to calling its |
Minimize graphics state changes while drawing | Changing the graphics state requires effort by the window server. If you need to draw content that uses similar state information, try to draw that content together to reduce the number of state changes needed. |
Providing high-quality images for your user interface should be a priority in your design. Images provide a reasonably efficient way to display complicated graphics and should be used wherever they are appropriate. When creating images for your application, keep the following guidelines in mind:
Use the PNG format for images. The PNG format provides high-quality image content and is the preferred image format for iOS. In addition, iOS includes an optimized drawing path for PNG images that is typically more efficient than other formats.
Create images so that they do not need resizing. If you plan to use an image at a particular size, be sure to create the corresponding image resource at that size. Do not create a larger image and scale it down to fit, because scaling requires additional CPU cycles and requires interpolation. If you need to present an image at variable sizes, include multiple versions of the image at different sizes and scale down from an image that is relatively close to the target size.
Quartz is the general name for the native window server and drawing technology in iOS. The Core Graphics framework is at the heart of Quartz, and is the primary interface you use for drawing content. This framework provides data types and functions for manipulating the following:
Graphics contexts
Paths
Images and bitmaps
Transparency layers
Colors, pattern colors, and color spaces
Gradients and shadings
Fonts
PDF content
UIKit builds on the basic features of Quartz by providing a focused set of classes for graphics-related operations. The UIKit graphics classes are not intended as a comprehensive set of drawing tools—Core Graphics already provides that. Instead, they provide drawing support for other UIKit classes. UIKit support includes the following classes and functions:
UIImage
, which implements an immutable class for displaying images
UIColor
, which provides basic support for device colors
UIFont
, which provides font information for classes that need it
UIScreen
, which provides basic information about the screen
Functions for generating a JPEG or PNG representation of a UIImage
object
Functions for drawing rectangles and clipping the drawing area
Functions for changing and getting the current graphics context
For information about the classes and methods that comprise UIKit, see UIKit Framework Reference. For more information about the opaque types and functions that comprise the Core Graphics framework, see Core Graphics Framework Reference.
By the time your drawRect:
method is called, your view’s built-in drawing code has already created and configured a default graphics context for you. You can retrieve a pointer to this graphics context by calling the UIGraphicsGetCurrentContext
function. This function returns a reference to a CGContextRef
type, which you pass to Core Graphics functions to modify the current graphics state. Table 2-3 lists the main functions you use to set different aspects of the graphics state. For a complete list of functions, see CGContext Reference. This table also lists UIKit alternatives where they exist.
Graphics state | Core Graphics functions | UIKit alternatives |
---|---|---|
Current transformation matrix (CTM) | None | |
Clipping area | None | |
Line: Width, join, cap, dash, miter limit | None | |
Accuracy of curve estimation (flatness) | None | |
Anti-aliasing setting | None | |
Color: Fill and stroke settings |
| |
Alpha value (transparency) | None | |
Rendering intent | None | |
Color space: Fill and stroke settings | None | |
Text: Font, font size, character spacing, text drawing mode |
| |
Blend mode | The |
The graphics context contains a stack of saved graphics states. When Quartz creates a graphics context, the stack is empty. Using the CGContextSaveGState
function pushes a copy of the current graphics state onto the stack. Thereafter, modifications you make to the graphics state affect subsequent drawing operations but do not affect the copy stored on the stack. When you are done making modifications, you can return to the previous graphics state by popping the saved state off the top of the stack using the CGContextRestoreGState
function. Pushing and popping graphics states in this manner is a fast way to return to a previous state and eliminates the need to undo each state change individually. It is also the only way to restore some aspects of the state, such as the clipping path, back to their original settings.
For general information about graphics contexts and using them to configure the drawing environment, see “Graphics Contexts” in Quartz 2D Programming Guide.
iOS provides support for loading and displaying images using both the UIKit and Core Graphics frameworks. How you determine which classes and functions to use to draw images depends on how you intend to use them. Whenever possible, though, it is recommended that you use the classes of UIKit for representing images in your code. Table 2-4 lists some of the usage scenarios and the recommended options for handling them.
Scenario | Recommended usage |
---|---|
Display an image as the content of a view | Use a |
Display an image as an adornment for part of a view | Load and draw the image using the |
Save some bitmap data into an image object | Use the If you prefer using Core Graphics, you can use the |
Save an image as a JPEG or PNG file | Create a |
The following example shows how to load an image from your application’s bundle. You can subsequently use this image object to initialize a UIImageView
object, or you can store it and draw it explicitly in your view’s drawRect:
method.
NSString* imagePath = [[NSBundle mainBundle] pathForResource:@"myImage" ofType:@"png"]; |
UIImage* myImageObj = [[UIImage alloc] initWithContentsOfFile:imagePath]; |
To draw an image explicitly in your view’s drawRect:
method, you can use any of the drawing methods available in UIImage
. These methods let you specify where in your view you want to draw the image and therefore do not require you to create and apply a separate transform prior to drawing. Assuming you stored the previously loaded image in a member variable called anImage
, the following example draws that image at the point (10, 10) in the view.
- (void)drawRect:(CGRect)rect |
{ |
// Draw the image |
[anImage drawAtPoint:CGPointMake(10, 10)]; |
} |
Important: If you use the CGContextDrawImage
function to draw bitmap images directly, the image data is inverted along the y axis by default. This is because Quartz images assume a coordinate system with a lower-left corner origin and positive coordinate axes extending up and to the right from that point. Although you can apply a transform before drawing, the simpler (and recommended) way to draw Quartz images is to wrap them in a UIImage
object, which compensates for this difference in coordinate spaces automatically. For more information on creating and drawing images using Core Graphics, see Quartz 2D Programming Guide.
A path is a description of a 2D geometric scene that uses a sequence of lines and Bézier curves to represent that scene. UIKit includes the UIRectFrame
and UIRectFill
functions (among others) for drawing simple paths such as rectangles in your views. Core Graphics also includes convenience functions for creating simple paths such as rectangles and ellipses. For more complex paths, you must create the path yourself using the functions of the Core Graphics framework.
To create a path, you use the CGContextBeginPath
function to configure the graphics context to receive path commands. After calling that function, you use other path-related functions to set the path’s starting point, draw lines and curves, add rectangles and ellipses, and so on. When you are done specifying the path geometry, you can paint the path directly or create a CGPathRef
or CGMutablePathRef
data type to store a reference to that path for later use.
When you want to draw a path in your view, you can stroke it, fill it, or do both. Stroking a path with a function such as CGContextStrokePath
creates a line centered on the path using the current stroke color. Filling the path with the CGContextFillPath
function uses the current fill color or fill pattern to fill the area enclosed by the path’s line segments.
For more information on how to draw paths, including information about how you specify the points for complex path elements, see “Paths” in Quartz 2D Programming Guide. For information on the functions you use to create paths, see CGContext Reference and CGPath Reference.
The Core Graphics framework includes additional functions for creating patterns, gradients, and shadings. You use these types to create non monochrome colors and use them to fill the paths you create. Patterns are created from repeating images or content. Gradients and shadings provide different ways to create smooth transitions from color to color.
The details for creating and using patterns, gradients, and shadings are all covered in Quartz 2D Programming Guide.
The Open Graphics Library (OpenGL) is a cross-platform C-based interface used to create 2D and 3D content on desktop systems. It is typically used by games developers or anyone needing to perform drawing with high frame rates. You use OpenGL functions to specify primitive structures such as points, lines, and polygons and the textures and special effects to apply to those structures to enhance their appearance. The functions you call send graphics commands to the underlying hardware, where they are then rendered. Because rendering is done mostly in hardware, OpenGL drawing is usually very fast.
OpenGL for Embedded Systems is a pared-down version of OpenGL that is designed for mobile devices and takes advantage of modern graphics hardware. If you want to create OpenGL content for iOS–based devices—that is, iPhone or iPod Touch—you’ll use OpenGL ES. The OpenGL ES framework (OpenGLES.framework
) provided with iOS supports both the OpenGL ES v1.1 and OpenGL ES v2.0 specifications.
For more information about OpenGL ES support in iOS, see OpenGL ES Programming Guide for iOS.
Core Animation is an Objective-C framework that provides infrastructure for creating fluid, real-time animations quickly and easily. Core Animation is not a drawing technology itself, in the sense that it does not provide primitive routines for creating shapes, images, or other types of content. Instead, it is a technology for manipulating and displaying content that you created using other technologies.
Most applications can benefit from using Core Animation in some form in iOS. Animations provide feedback to the user about what is happening. For example, when the user navigates through the Settings application, screens slide in and out of view based on whether the user is navigating further down the preferences hierarchy or back up to the root node. This kind of feedback is important and provides contextual information for the user. It also enhances the visual style of an application.
In most cases, you may be able to reap the benefits of Core Animation with very little effort. For example, several properties of the UIView
class (including the view’s frame, center, color, and opacity—among others) can be configured to trigger animations when their values change. You have to do some work to let UIKit know that you want these animations performed, but the animations themselves are created and run automatically for you. For information about how to trigger the built-in view animations, see “Animating Views.”
When you go beyond the basic animations, you must interact more directly with Core Animation classes and methods. The following sections provide information about Core Animation and show you how to work with its classes and methods to create typical animations in iOS. For additional information about Core Animation and how to use it, see Core Animation Programming Guide.
The key technology in Core Animation is the layer object. Layers are lightweight objects that are similar in nature to views, but that are actually model objects that encapsulate geometry, timing, and visual properties for the content you want to display. The content itself is provided in one of three ways:
You can assign a CGImageRef
to the contents
property of the layer object.
You can assign a delegate to the layer and let the delegate handle the drawing.
You can subclass CALayer
and override one of the display methods.
When you manipulate a layer object’s properties, what you are actually manipulating is the model-level data that determines how the associated content should be displayed. The actual rendering of that content is handled separately from your code and is heavily optimized to ensure it is fast. All you must do is set the layer content, configure the animation properties, and then let Core Animation take over.
For more information about layers and how they are used, see Core Animation Programming Guide.
When it comes to animating layers, Core Animation uses separate animation objects to control the timing and behavior of the animation. The CAAnimation
class and its subclasses provide different types of animation behaviors that you can use in your code. You can create simple animations that migrate a property from one value to another, or you can create complex keyframe animations that track the animation through the set of values and timing functions you provide.
Core Animation also lets you group multiple animations together into a single unit, called a transaction. The CATransaction
object manages the group of animations as a unit. You can also use the methods of this class to set the duration of the animation.
For examples of how to create custom animations, see Animation Types and Timing Programming Guide.
Last updated: 2010-07-07