iOS Reference Library Apple Developer
Search

Graphics and Drawing

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.

The UIKit Graphics System

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 View Drawing Cycle

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:

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.

Coordinates and Coordinate Transforms

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.

Graphics Contexts

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.

Points Versus Pixels

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.

Color and Color Spaces

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.

Supported Image Formats

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.

Table 2-1  Supported image formats

Format

Filename extensions

Portable Network Graphic (PNG)

.png

Tagged Image File Format (TIFF)

.tiff, .tif

Joint Photographic Experts Group (JPEG)

.jpeg, .jpg

Graphic Interchange Format (GIF)

.gif

Windows Bitmap Format (DIB)

.bmp, .BMPf

Windows Icon Format

.ico

Windows Cursor

.cur

XWindow bitmap

.xbm

Drawing Tips

The following sections provide tips on how to write quality drawing code while ensuring that your application looks appealing to end users.

Deciding When to Use Custom Drawing Code

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.

Improving Drawing Performance

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.

Table 2-2  Tips for improving drawing performance

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 drawRect: method of UIView to do your drawing, use the update rectangle passed to that method to limit the scope of your drawing. For OpenGL drawing, you must track updates yourself.

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 opaque property of the view must be set to YES.

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 drawRect: method to update that same area. If you are responding to scrolling events in your view, clearing this region repeatedly during scrolling updates can be expensive. To disable the behavior, you can change the value in the clearsContextBeforeDrawing property to NO.

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.

Maintaining Image Quality

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:

Drawing with Quartz and UIKit

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:

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:

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.

Configuring the Graphics Context

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.

Table 2-3  Core graphics functions for modifying graphics state

Graphics state

Core Graphics functions

UIKit alternatives

Current transformation matrix (CTM)

CGContextRotateCTM

CGContextScaleCTM

CGContextTranslateCTM

CGContextConcatCTM

None

Clipping area

CGContextClipToRect

None

Line: Width, join, cap, dash, miter limit

CGContextSetLineWidth

CGContextSetLineJoin

CGContextSetLineCap

CGContextSetLineDash

CGContextSetMiterLimit

None

Accuracy of curve estimation (flatness)

CGContextSetFlatness

None

Anti-aliasing setting

CGContextSetAllowsAntialiasing

None

Color: Fill and stroke settings

CGContextSetRGBFillColor

CGContextSetRGBStrokeColor

UIColor class

Alpha value (transparency)

CGContextSetAlpha

None

Rendering intent

CGContextSetRenderingIntent

None

Color space: Fill and stroke settings

CGContextSetFillColorSpace

CGContextSetStrokeColorSpace

None

Text: Font, font size, character spacing, text drawing mode

CGContextSetFont

CGContextSetFontSize

CGContextSetCharacterSpacing

UIFont class

Blend mode

CGContextSetBlendMode

The UIImage class and various drawing functions let you specify which blend mode to use.

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.

Creating and Drawing Images

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.

Table 2-4  Usage scenarios for images

Scenario

Recommended usage

Display an image as the content of a view

Use a UIImageView class to load and display the image. This option assumes that your view’s only content is an image. You can still layer other views on top of the image view to draw additional controls or content.

Display an image as an adornment for part of a view

Load and draw the image using the UIImage class.

Save some bitmap data into an image object

Use the UIGraphicsBeginImageContext function to create a new image-based graphics context. After creating this context, you can draw your image contents into it and then use the UIGraphicsGetImageFromCurrentImageContext function to generate an image based on what you drew. (If desired, you can even continue drawing and generate additional images.) When you are done creating images, use the UIGraphicsEndImageContext function to close the graphic context.

If you prefer using Core Graphics, you can use the CGBitmapContextCreate function to create a bitmap graphics context and draw your image contents into it. When you finish drawing, use the CGBitmapContextCreateImage function to create a CGImageRef from the bitmap context. You can draw the Core Graphics image directly or use it to initialize a UIImage object.

Save an image as a JPEG or PNG file

Create a UIImage object from the original image data. Call the UIImageJPEGRepresentation or UIImagePNGRepresentation function to get an NSData object, and use that object’s methods to save the data to a file.

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.

Creating and Drawing Paths

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.

Creating Patterns, Gradients, and Shadings

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.

Drawing with OpenGL ES

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.

Applying Core Animation Effects

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.

About Layers

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:

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.

About Animations

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

Did this document help you? Yes It's good, but... Not helpful...