Chapter 13. Graphics: Quartz, Core Animation, and OpenGL

 

This chapter covers

  • Using Quartz 2D for drawing
  • Understanding context, paths, and state
  • Using Core Animation
  • Learning about OpenGL ES

 

As you saw in chapter 11, creating and displaying images often isn’t enough. In games and other more complex programs, you’ll also want to manipulate those images in various ways at runtime. In iOS there are two major ways to do this.

The first is through Quartz 2D, a two-dimensional drawing library that allows for complex line drawings, much as Canvas did on the web. It’s also the heart of the Core Graphics frameworks. We already touched on Quartz in chapter 11, when you drew images straight to the CALayer of a UIView; it will be the focus of the majority of this chapter. Quartz also supports Core Animation functions, which we’ll address somewhat more briefly.

The second major way to manipulate images is through the OpenGL ES API. This cross-platform API, originally developed by Silicon Graphics, could be the topic of its own book, so we’ll only show you how to get started with it.

But most of this chapter will be about Quartz. We’ll look at drawing paths, setting the graphical state, and more advanced drawing techniques. We’ll dive into Quartz immediately.

13.1. An introduction to Quartz 2D

Quartz 2D is a two-dimensional drawing library that’s tightly integrated into iOS. It works well with all the relevant frameworks, including Core Animation, OpenGL ES, and the UIKit.

Fundamentally, Quartz’s drawings depend on three core ideas: context, paths, and state, each of which will be the topic of a future section:

  • Context is a description of where the graphics are being written to, as defined by a CGContextRef. You’ll usually be writing to a UIView or to a bitmap.
  • Layers are a little less important for this overview, but they’re where Quartz drawing occurs. They can be stacked one on top of another, creating a complex result. When working with the iPhone or iPad, you’ll often only have a single layer associated with each of your UIKit objects.
  • Paths are what you’ll typically draw in Quartz. These are collections of lines and arcs that are drawn in advance and then are painted to the screen by either stroking or filling the path in question (or, possibly, by clipping it).
  • State saves the values of transformations, clipping paths, fill and stroke settings, alpha values, other blending modes, text characteristics, and more. The current state can be stored with CGContextSaveGState and restored with CGContextRestoreGState, allowing for easy switching among complex drawing setups.

Quartz is built on the older Core Foundation framework that you’ve met a few times over the course of this part of the book. This means you’ll need to use older styles of variables to integrate with Cocoa Touch using toll-free bridging, and to respect Core Foundation’s memory-management techniques.

If you need more information about any Quartz topic, see the “Quartz 2D Programming Guide” at Apple’s developer website. It’s a fine introduction to Quartz, although not as focused as you’d probably like, a deficiency that we’ll correct in this chapter.

Using Quartz requires little special setup. It can be easily integrated into any template and any project you want. Be sure to include the Core Graphics framework and the CoreGraphics/CoreGraphics.h include file before you get started.

With that said, we’re ready to dive into our first major Quartz topic: the context.

13.2. The Quartz context

A graphical context is a description of where Quartz writes to. This could include a printer, a PDF file, a window, or a bitmap image. On the iPhone and iPad, you’re only likely to use two of these possibilities.

Most frequently, you’ll work with the graphical context that’s automatically associated with the CALayer (Core Animation layer) of each UIView. That means you can use Quartz to draw to most UIKit objects. To do so, you override the drawRect: method and, inside the object in question, use UIGraphicsGetCurrentContext to retrieve the current context.

You may alternatively create a bitmap context in order to create or modify an image that you’ll use elsewhere in your program. You do this by using the UIGraphicsBeginImageContext and UIGraphicsEndImageContext functions.

You can use a variety of Core Graphics functions to access other sorts of contexts—types that you won’t usually use on an iPhone or iPad. The functions required to capture a PDF context are one such example. These have two deficits you should be aware of: they depend more heavily on the Core Foundation frameworks, and they use Quartz’s inverted coordinate system.

 

Warning: inverse coordinate system ahead

By now, you should be familiar with the standard iOS coordinate system. It has the origin at upper left on the screen, with the main axes running to the right and down. Quartz’s default coordinate system is inverted, with the origin at lower left and the main axes running right and up.

This isn’t usually a problem. The Cocoa Touch methods you use to create and write to graphical contexts usually transform Quartz’s default coordinates so that they look like iPhone coordinates to you.

Once in a while, though, you’ll run into a situation where you’ll draw to a UI-derived context and find your content flipped upside down (and in the wrong position). This is a result of accessing Quartz in a way that hasn’t been transformed.

As of this writing, we’re aware of two situations where you’ll have to correct Quartz’s coordinate system by yourself, even when using one of the UI-derived contexts: if you import images using the native Quartz functions (as opposed to the UIImage methods you saw in chapter 11), and if you write text. We’ll talk about each of these when we get to them.

Personally, we consider these coordinate inversions bugs, and it’s our expectation that they’ll eventually be corrected.

If you create a context without using Cocoa Touch, expect everything to be inverted. This is something that we don’t expect to change in the future.

 

One thing to note about graphical contexts is that they’re created in a stack: when you create a new context, it’s pushed on top of a stack, and when you’ve finished with it, it’s popped off. This means that if you create a new bitmap context, it’s placed on top of any existing context, such as the one associated with your UIView, and stays there until you’ve finished with the bitmap.

Table 13.1 lists these context-related functions, including both the standard UI context functions and the older Core Graphics function you’re most likely to use—for PDFs.

Table 13.1. Methods for graphical context creation

Function

Arguments

Summary

UIGraphicsGetCurrentContext (none) Returns the current context, which is usually the context of the current UIKit object but can also be a context that you create by hand
UIGraphicsBeginImageContext CGSize Creates a bitmap context
UIGraphicsEndImageContext (none) Pops a bitmap context off the stack
UIGraphicsGetImageFromCurrentImageContext (none) Returns a bitmap as a UIImage *; used with a bitmap context only
CGPDFContextCreate CGDataConsumerRef, CGRect, CGDictionaryRef Creates a PDF context

We won’t cover PDFs in this book, but we’ll look at how to use each of the UIKit context styles, starting with the UIView.

13.2.1. Drawing to a UIView

In chapter 11, we offered an introductory example of how to write to a UIView graphical context using the drawRect: method. That example was somewhat simplified because the UIKit draw-image commands mostly hide the idea of graphical contexts from you. They automatically write to the current context, which inside drawRect: is the context related to the UIView. For most other functions, you need to do a bit more work: retrieving the graphical context and passing that context along to any drawing commands that you use.

Here’s how to draw a simple abstract face using this technique:

- (void)drawRect:(CGRect)rect {
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGContextBeginPath(ctx);
    CGContextAddArc(ctx,110,50,30,0,2*M_PI,1);
    CGContextAddArc(ctx,210,50,30,0,2*M_PI,1);
    CGContextAddArc(ctx,160,110,15,0,2*M_PI,1);
    CGContextAddArc(ctx,160,210,25,0,2*M_PI,1);
    CGContextFillPath(ctx);
}

This example is fairly simple. You create a UIView subclass, and then you go to its drawRect: method. Once there, you capture the current context and use it to do whatever Quartz 2D drawing you desire.

The function calls won’t be familiar to you, but they’re calls to draw a bunch of circles; we’ll discuss them in the next section. As shown in figure 13.1, the art ends up looking oddly abstract, which shows how Quartz draws continuous paths. You see lines connecting one circle to the next, as if the pencil never comes off the page, a topic we’ll talk about more in the next section.

Figure 13.1. The iPhone does abstract art.

Leaving aside those specifics for a moment, this shows one of the two ways you can use all the Quartz functions described in this chapter: by painting a UIView. And remember that a UIView can be almost any UIKit object, due to inheritance.

Drawing to a UIView allows for on-screen picture creation, but you can also draw pictures without displaying them immediately. That’s done with a bitmap.

13.2.2. Drawing to a bitmap

The main reason to create a bitmap rather than draw directly to a view is to use your graphic several times in your program—perhaps all at the same time. For example, Apple offers a sample program that draws the periodic table by creating a standard bitmap that’s used for all the elements and then repeating it. You might similarly create billiard balls using bitmaps if you were programming a billiards game. In chapter 10, you could have used Quartz to create the dots that you used in the gravity and altitude programs as bitmaps, so that you didn’t have to separately create them outside the program.

The process of creating a bitmap and turning it into a UIImage is relatively simple. You create a graphical context, draw in that context, save the context to an image, and close the context. The following code shows how to create a red dot image like the one you used in earlier programs:

- (void)viewDidLoad {
    [super viewDidLoad];
    UIGraphicsBeginImageContext(CGSizeMake(20,20));
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGContextBeginPath(ctx);
    CGContextAddArc(ctx,10,10,10,0,2*M_PI,1);
    CGContextSetRGBFillColor(ctx, 1, 0, 0, 1);
    CGContextFillPath(ctx);
    UIImage *redBall =
        UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    UIImageView *redBallView = [[UIImageView alloc] initWithImage:redBall];
    redBallView.center = CGPointMake(160,330);
    [self.view addSubview:redBallView];
}

Again, this example is simple. You could do this work anywhere you wanted, but we’ve elected to use the viewDidLoad setup method. To start the process, you create an image context, which is to say a bitmap, and you immediately retrieve that context’s variable for use. Following that, you do whatever drawing work you want. When you’ve finished, you turn the bitmap into a UIImage and close out your context. You can then manipulate the image as you see fit; here it’s turned into a UIImageView.

You now know two ways to use contexts in the Quartz environment. With that in hand, you’re ready to dive straight into what Quartz can do, starting with paths, which are the foundation of most Quartz work.

13.3. Drawing paths

The path is what Quartz draws. If you’ve worked with Canvas, this will look familiar, because both libraries use the same drawing paradigm. A path is a set of lines, arcs, and curves that are all placed continuously within a graphical context. You only paint a path when it’s complete, at which point you can choose to either fill it or stroke it.

Many of the functions required to define and draw paths are listed in table 13.2. CGContextMoveToPoint is the one function that deserves some additional discussion. As you’ll recall, we said that a path is a continuous series of lines and arcs that you draw without picking the pen up off the paper. But there is a way to pick up the pen, and that’s with the CGContextMoveToPoint function, which is vital when you want to draw unconnected objects as part of a single path.

Table 13.2. A variety of simple drawing functions that allow for vector-based graphics

Function

Arguments

Summary

CGContextBeginPath context Creates a new path.
CGContextAddArc context, x, y, radius, startangle, endangle, clockwise Creates an arc, with the angles defined in radians. A line is drawn to the start point if there are previous entries in the path and from the end point if there are additional entries.
The more complex functions CGContextAddArcToPoint, CGContextAddCurveToPoint, and CGContextAddQuadCurveToPoint allow for the creation of tangential arcs, Bezier curves, and quadratic Bezier curves.
CGContextAddEllipseInRect context, CGRect Creates an ellipse that fits inside the rectangle.
CGContextAddLineToPoint context, x, y Creates a line from the current point to the designated end point.
The more complex CGContextAddLines function allows the addition of an array of lines.
CGContextAddRect context, CGRect Creates a rectangle.
The more complex CGContextAddRects function adds a series of rectangles.
CGContextMoveToPoint context, x, y Moves to the point without drawing.

For example, to avoid drawing a line between the first two circles in the earlier abstract-art example, you can use the following code:

CGContextAddArc(ctx,110,50,30,0,2*M_PI,1);
CGContextMoveToPoint(ctx, 240, 50);
CGContextAddArc(ctx,210,50,30,0,2*M_PI,1);

After drawing the first circle, you move your virtual pencil to the point where you begin drawing the arc of the second circle, which is 240, 50.

The rest of the functions are largely self-explanatory. You already saw the arc commands in some of the earlier examples, and the others work in similar ways. For more information about the more complex functions, look at the CGContext class reference.

We’ll move on from these simple drawing commands to the question of what you do once you have a path. You have several options, beginning with the simple possibility of closing it and drawing it.

13.3.1. Finishing a path

As we’ve already noted, the path functions define the points and lines that make up a drawing. When you have that in hand, you have to do something with it. There are three main choices: stroke the path, fill the path, or turn it into a clipping path. These functions are all listed in table 13.3.

Table 13.3. Functions for finishing a path

Function

Arguments

Summary

CGContextClosePath context Draws a line from the end point of your path to the start point, and then closes it. This is an optional final command that’s usually used when you’re stroking a path.
CGContextFillPath context Closes your path automatically, and paints it by filling it in. CGContextEOFillPath is an alternative that does the filling in a slightly different way.
CGContextStrokePath context Paints your path by stroking it.
CGContextClip context Turns the current path into a clipping path.

You’ll usually either stroke (outline) a path or fill it when you’ve finished. You used a fill in each of the previous examples, but a stroke could be substituted; the difference is that the circles wouldn’t be filled in.

A clipping path is a bit more complex, in that you don’t draw something on the screen. Instead, you define an area, which corresponds to the area inside the path that you’d have filled in, and you only show later drawings that appear inside that clipping path. We’ll talk about clipping paths more, and show an example, when we get to graphical states. For now, note that you create them from paths. Creating reusable paths So far, you’ve created paths by drawing them directly to a context, be it a UIView or a bitmap. But it’s also possible to create reusable paths that you can quickly and easily apply later. This has many of the same advantages as creating a bitmap: you get reusability and multiplicity. Reusable paths are particularly useful in animations and programs where you use the same graphic on multiple pages.

To create reusable paths, you use the CGPath commands rather than the CGCon-text commands. There are equivalents to many of the simple CGContext functions, as shown in table 13.4.

Table 13.4. CGPath commands and their CGContext equivalents

CGPath function

CGContext function

CGPathCreateMutable CGContextBeginPath
CGPathAddArc CGContextAddArc
CGPathAddEllipseInRect CGContextAddEllipseInRect
CGPathAddLineToPoint CGContextAddLineToPoint
CGPathAddRect CGContextAddRect
CGPathMoveToPoint CGContextMoveToPoint
CGPathCloseSubpath CGContextClosePath

When you’re working with reusable paths, you first use the CGPathCreateMutable function to create a CGPathRef, and then you use CGPath commands to add lines or arcs to that CGPathRef. The reusable path can include multiple, discrete subpaths that don’t have to connect to each other. You can end one subpath and start another with the CGPathCloseSubpath function.

Note that no painting functions are associated with the reusable paths. That’s because they’re storage devices. To use one, you add it to a normal path with the CGContextAddPath function, which draws your stored path to your graphical context, where it abides by the normal rules.

The following code uses a mutable path to replace the CGContext commands that you previously used to draw an abstract face. A more realistic example would probably hold onto the path for use elsewhere; you release it here as a reminder of how Core Foundation memory management works:

- (void)drawRect:(CGRect)rect {
    CGMutablePathRef myPath = CGPathCreateMutable();
    CGPathAddArc(myPath,NULL,110,50,30,0,2*M_PI,1);
    CGPathMoveToPoint(myPath,NULL, 240, 50);
    CGPathAddArc(myPath,NULL,210,50,30,0,2*M_PI,1);
    CGPathAddArc(myPath,NULL,160,110,15,0,2*M_PI,1);
    CGPathAddArc(myPath,NULL,160,210,25,0,2*M_PI,1);

    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGContextBeginPath(ctx);
    CGContextAddPath(ctx,myPath);
    CGContextStrokePath(ctx);
    CFRelease(myPath);
}

Of note here is the NULL that’s constantly being sent as a second argument to the CGPath commands. This argument is intended to be a CGAffineTransform variable. It allows you to apply a transformation to the element being drawn, which is something we’ll discuss shortly.

Now that we’ve looked at two different ways to create complex paths, we’ll take a step back and look at how to draw much simpler objects in a simpler way.

13.3.2. Drawing rectangles

Drawing paths takes some work, but if you want to draw a rectangle, Quartz makes it easy. All you have to do is use one of a few functions listed in table 13.5. These functions take care of the path creation, drawing, and painting for you in a single step.

Table 13.5. Specific functions allow you to draw rectangles

Function

Arguments

Summary

CGContextClearRect context, CGRect Erases a rectangle.
CGContextFillRect context, CGRect Draws a filled rectangle.
The more complex variant CGContextFillRects allows you to fill a whole array of rectangles.
CGContextStrokeRect context, CGRect Draws a stroked rectangle.
CGContextStrokeRectWithWidth context, CGRect, width Draws a stroked rectangle, with the stroke being the designated width.

The CGContextClearRect function can be particularly useful for erasing a window when you’re ready to draw something new to it. Now that we’ve told you how to draw objects in the simplest way possible, we’re ready to move on and discuss how to draw objects in more complex ways—by modifying state.

13.4. Setting the graphical state

The graphical state is how Quartz draws. It includes a variety of information such as what colors are used for fills or strokes, which clipping paths constrain the current drawing path, what transformations are applied to the drawing, and a number of other less-important variables.

State is maintained in a stack. You can save a state at any time; it doesn’t change how things are being drawn, but it does push that current state onto the top of a stack for later retrieval. Later, you can restore a state, which pops the top state off the stack, putting things back to how they were before the last save. We’ve mentioned these functions before, but we’ve also listed them here in table 13.6.

Table 13.6. State-related functions that help define how you draw

Function

Arguments

Summary

CGContextSaveGState context Pushes the state onto a stack
CGContextRestoreGState context Pops the state off a stack

As we’ve already noted, you can store a lot of things in graphical state. We’ll cover many of them here, starting with colors.

13.4.1. Setting colors

In Quartz, you select colors by setting the fill color, the stroke color, or both in the current graphic state. After you’ve done this, any fill or stroke commands following the color commands appear in the appropriate colors. Note that color is irrelevant while you’re drawing the individual elements of a path—the color commands apply only to the painting of the complete path at the end.

You can select colors from a variety of color spaces, which are different ways to choose colors. They include RGB (red-green-blue), RGBA (red-green-blue-alpha), CMYK (cyan-magenta-yellow-black), and CGColor (the underlying Core Graphics color model). On the iPhone and iPad, you’ll usually either use the RGBA color space or use a command that lets you select a color using standard UIKit methods. Table 13.7 lists the four most relevant of these functions.

Table 13.7. The most important of numerous coloring functions

Function

Arguments

Summary

CGContextSetRGBFillColor context, red, green, blue, alpha Sets the fill to the RGBA value
CGContextSetRGBStrokeColor context, red, green, blue, alpha Sets the stroke to the RGBA value
CGContextSetFillColorWithColor context, CGColor Sets the fill to the CGColor
CGContextSetStrokeColorWithColor context, CGColor Sets the stroke to the CGColor

The two RGB functions allow you to set a color using values from 0 to 1 for each of red, green, blue, and alpha transparency (opacity). You saw an example of this earlier:

CGContextSetRGBFillColor(ctx, 1, 0, 0, 1);

The last two functions in table 13.7 allow you to set the color using any CGColor, and you’ll understand how useful that is when you realize that you can read a CGColor property from any UIColor you create:

CGContextSetFillColorWithColor(ctx, [[UIColor redColor] CGColor]);

Given that you’re already familiar and comfortable with the UIColors, we expect that this latter function will be a popular one.

Having now covered the main ways to apply colors to your graphical state, we’re ready to move on to the next topic: how to change how you draw through graphical state transformations.

13.4.2. Making transformations

Transformations modify how you draw to your graphic context. They do this by changing the grid on which you’re drawing by moving its origin, rotating, or resizing.

Why would you want to do these transformations?

  • They can be useful for drawing photographs (or other images), because they allow you to scale or rotate the picture.
  • They can make it a lot easier to do certain types of mathematical drawing. For example, it’s probably easier to draw a symmetric mathematical construct if you have your origin in the center of the screen rather than in the upper-left corner.
  • They can allow you to flip your screen if you end up in a context (or using a function) with an inverse coordinate system.
CTM Transformations

The simplest way to apply a transformation is to use one of the functions that modify the current transformation matrix (CTM), which is a matrix that’s applied to all drawing done in your current graphical state. These functions are described in table 13.8.

Table 13.8. CTM transformation functions that allow you to change how you draw

Function

Arguments

Summary

CGContextRotateCTM context, radian rotation Rotates the grid
CGContextScaleCTM context, x-scale, y-scale Scales the grid
CGContextTranslateCTM context, x-change, y-change Moves the origin

There are two gotchas that you should watch for.

First, note that the ordering of transformations is somewhat pickier than the ordering of color commands. You need to start your transformation before you add the relevant lines to your path, and you need to maintain it until after you paint that path.

Second, although these transformations can be applied in any sequence, order matters. Following are two transformation commands that can be applied together:

CGContextTranslateCTM(ctx, 100, 100);
CGContextRotateCTM(ctx, .25*M_PI);

These functions move a drawing 100 to the right and 100 down and rotate it by 45 degrees. Figure 13.2 shows the untransformed picture (which you’ve seen before), the results if these commands are applied with the translation before the rotation, and the results if they’re applied in the opposite order.

Figure 13.2. As these variant transformations show, order matters. The left picture is untransformed, the middle one is translated and then rotated, and the right one is rotated and then translated.

Clearly, you need to be careful and think about ordering when you’re applying CTM transformations.

But CTM transformations aren’t the only way to change your drawing space.

Affine Transformations

Just as you can create a reusable path and then apply that to the context with the CGContextAddPath function, you can also create a reusable transformation matrix (using the affine transformation functions) and then apply that to the context with the CGContextConcatCTM function. This is managed by a set of six core functions, listed in table 13.9. Half of them create a new matrix, applying a transformation at the same time, and the other half apply a transformation to an existing matrix. The last function is the one that applies an affine transformation to your current graphical state.

Table 13.9. Affine transformations for creating reusable transformations

Function

Arguments

Summary

CGAffineTransformMake-Rotation radian rotation Makes an array with the rotation
CGAffineTransformMakeScale x-scale, y-scale Makes an array with the scale
CGAffineTransformMake-Translation x-change, y-change Makes an array with the translation
CGAffineTransformRotate array, radian rotation Rotates the array
CGAffineTransformScale array, x-scale, y-scale Scales the array
CGAffineTransformTranslate array, x-change, y-change Translates the array
CGContextConcatCTM context, array Applies the transformation

The following code applies a rotation followed by a translation using a reusable affine matrix:

CGAffineTransform myAffine = CGAffineTransformMakeRotation(.25*M_PI);
CGAffineTransformTranslate(myAffine, 100, 100);
CGContextConcatCTM(ctx, myAffine);

In addition to creating reusable affine transformations, you can also modify the transforms at a much lower level. Any affine transformation is constructed from a 3 × 3 matrix that’s then multiplied across the individual vectors of your path using matrix multiplication. If you have specific needs, you can use the CGAffineTransformMake function to create a matrix by hand. Using it looks like this:

CGAffineTransform flip = CGAffineTransformMake(1,0,0,-1,0,0);

You can find information about how the matrix works and about some other functions in the CGAffine reference.

The next sort of state you may want to change is one that makes fairly large-scale changes to your drawings: the clipping path.

13.4.3. Setting clipping paths

We already spoke about clipping paths in section 13.3. You create a path as usual, but then you clip it, rather than filling it or stroking it. Anything you paint on the screen afterward (within that graphical state) appears only if it’s inside the clipping path.

For example, the following code causes later painting to appear only inside a large circle centered on the screen:

CGContextBeginPath(ctx);
CGContextAddArc(ctx,160,240,160,0,2*M_PI,1);
CGContextClip(ctx);

Figure 13.3 shows what a full-screen image looks like before clipping and after.

Figure 13.3. An example of a clipping path in use. The unclipped image is on the left, and the clipped image is on the right.

As with most of these Quartz functions, you have some opportunities for subtleties when using clipping paths. The CGContext reference offers a few additional functions for creating and modifying clipping paths.

So far, we’ve discussed all the big-picture options for modifying your graphical state. You can do many smaller things, too.

13.4.4. Other settings

A wide variety of additional settings can be used as part of the graphical state. Table 13.10 lists many of the most interesting ones.

Table 13.10. A selection of other ways to change state

Function

Arguments

Summary

CGContextSetAlpha context, alpha Sets alpha transparency
CGContextSetBlendMode context, CGBlendMode Sets blending to one of almost 30 values, which specify how objects laid on top of each other interact with each other
CGContextSetFlatness context, flatness Defines the accuracy of curves
CGContextSetLineCap context, CGLineCap Defines how to draw the end of a line
CGContextSetLineDash context, phase, lengths array, count Describes how to draw dashes along a stroke
CGContextSetLineJoin context, CGLineJoin Defines how lines come together
CGContextSetLineWidth context, width Describes the width of a stroke
CGContextSetShadow context, CGSize, blur Sets a shadow behind all drawings
CGContextSetShadowWithColor context, CGSize, blur, color Sets a colored shadow behind all drawings

You can also find a number of more complex state changes in the CGContext class reference, but we’ve described the ones you’re most likely to use in the course of an average program.

We’re drawing to a close on the topic of graphical state, so let’s step back for a moment and look at how graphical state works.

13.4.5. Managing the state

When you use any of the various functions that modify the graphical state, you’re changing how you paint inside your current graphical context. The functions change the colors you’re using, they transform your underlying grid, they clip the area you’re allowed to paint within, or they make various smaller changes.

You can constantly reset these variables as your needs change, but this can get annoying. That’s why you should use the stack of states. It allows you to make many changes to state and then revert to a previous setup that you were happy with. We’ve already shown the two functions that do this in table 13.6.

Remember to save the state before you make a big change, such as adding a clipping path or running a whole bunch of graphical state functions. Then, restore the state when you’ve done that. If you want, you can even be clever and slowly build up a set of states in your stack and move back through them appropriately.

You should now understand the three most important elements of drawing with Quartz: contexts, which specify where to draw; paths, which specify what to draw; and graphical states, which specify how to draw. You can do numerous more advanced things in Quartz, and although we won’t get to all of them, the next section covers the most interesting ones.

13.5. Advanced drawing in Quartz

Quartz has a number of advanced capabilities that go beyond simple line drawings. In this section, we’ll look at using gradients, images, and words.

13.5.1. Drawing gradients

Gradients are a core part of SDK design, because they’re a clearly evident aspect of the standard user interface. Unfortunately, there’s no UIKit-level class for creating gradients; instead, you have to fall back on Quartz.

You can create gradients in Quartz in two ways: using a CGShadingRef object or a CGGradientRef object. As is often the case in Core Foundation functions, the difference is in complexity. CGGradientRef allows you to draw simple gradients, and CGShadingRef requires you to define a CGFunctionRef object to precisely calculate how the colors in the gradient are displayed. As you’ve probably guessed, we’ll talk about CGGradientRef here and point you to the Apple class references for CGShadingRef.

Table 13.11 shows the important functions required to draw gradients with CGGradientRef.

Table 13.11. CGColorSpace, CGGradient, and CGContext functions for drawing gradients

Function

Arguments

Summary

CGColorSpaceCreateWithName color space constant Creates a color space by name
CGGradientCreateWithColors color space, color array, location array Creates a gradient using pregenerated colors
CGGradientCreateWithColorComponents color space, color components array, location array, color count Creates a gradient with an array of color parts
CGContextDrawLinearGradient context, gradient, start CGPoint, end CGPoint, options Draws a linear gradient
CGContextDrawRadialGradient context, gradient, start center, start radius, end center, end radius, options Draws a radial gradient
CGColorSpaceRelease color space Frees up a color space object
CGGradientRelease gradient Frees up a gradient object

Drawing a gradient is a four-step process:

  1. Define the color space, which you usually do by calling CGColorSpaceCreateDeviceRGB for the iPhone and iPad.
  2. Define the gradient by listing colors and where they appear in the gradient, from 0 to 1. You can do this two ways. You can hand off an array of CGColors (which may be useful if you want to generate them using UIColors), or you can hand off a longer array that defines the colors using another method, such as RGBA.
  3. Draw the gradient as a linear gradient (going from point to point) or a radial gradient (going from the center to the edge of a circle).
  4. Free up the memory.

The following code shows the steps required to draw a three-color linear gradient that spans an entire iPhone screen:

CGColorSpaceRef myColorSpace =
    CGColorSpaceCreateDeviceRGB();
CGFloat components[12] = {1,0,0,1,
    0,1,0,1,
    0,0,1,1};
CGFloat locations[3] = {0,.5,1};
CGGradientRef myGradient =
    CGGradientCreateWithColorComponents(myColorSpace,
        components, locations, (size_t)3);
CGContextDrawLinearGradient(ctx, myGradient, CGPointMake(0,0),
    CGPointMake(320,480), (CGGradientDrawingOptions)NULL);
CGColorSpaceRelease(myColorSpace);
CGGradientRelease(myGradient);

This code steps through the steps we just listed, defining the color space, creating the parts of the gradient, drawing it, and cleaning up after it. As usual, you can find more info about gradients in the CGGradient reference. For now, though, we’re ready to move on to the next advanced category of Quartz work: images.

13.5.2. Drawing images

In chapter 11, you saw one way to work with images, using methods that largely hid the specifics of graphical contexts from you as a programmer. Now that you’re fully immersed in Quartz, you can choose to use the Core Graphics functions instead.

The Image Functions

The two major Core Graphics functions for drawing are listed in table 13.12.

Table 13.12. Two image functions in Quartz

Function

Arguments

Summary

CGContextDrawImage context, CGRect, image Draws an image scaled to fit the rectangle
CGContextDrawTiledImage context, CGRect, image Draws an image scaled to fit the rectangle but filling the current clip region

These functions both require a CGImageRef, but remember that you can use the CGImage property of a UIImage to produce one. Alternatively, you can use the commands described in the CGImage reference, which offer more precise functionality, to create a new CGImage. Our suggestion is to go with what you know, which means using the UIKit methods, unless they can’t do what you need.

There’s one big gotcha to using the Quartz-related image-drawing functions: they produce a flipped image because they use Quartz’s native coordinate system internally. We’ll show you how to fix that momentarily.

Drawing on a Bitmap

Often, you’ll want to turn an image into a bitmap and modify it before displaying it on the screen, most frequently so that you can make multiple uses of the image. We’ll offer a quick example of crossing out a picture here.

Part of what’s unique about this example is that you can do all your drawing work without ever showing the image to the user (unlike if you were drawing on a UIView), thus opening up the possibility of many image-editing functions. When you do decide to display your newly saved image, you’ll see results like the image in figure 13.4.

Figure 13.4. You can change a UIImage without showing it to the user.

The code needed to accomplish this simple crossing out is shown in the next listing.

Listing 13.1. Using bitmaps to edit images

The process of modifying an image involves relatively few steps. You start by creating your bitmap context. Next, you apply any transformations that you want to use for the picture . If you want to rotate or scale the original picture, here’s where you do it. Likewise, you can use a combination of translations and the context size to easily crop an image. In this example, you flip the picture over by applying a rotation and a translation, to account for the fact that CGContextDrawImage produces an inverted picture. (You’ll see an alternative way to do this in the next example.)

When your transformations are finished, you can draw your image and then draw whatever you want on top of it (or modify it in some other way). Finally, you save the new picture.

We’ll return to the idea of drawing on pictures in section 13.6 (though we’ll do it in a much more interactive way), but in the meantime we’re ready to draw words.

13.5.3. Drawing words

Unlike Canvas, Quartz supports drawing words on top of your pictures. The functions required are intricate, though, and we generally suggest using UILabel or other UIKit objects and placing them on top of your Quartz objects. But if you need words in Quartz (either because you’re interweaving the words with other Quartz content or because you’re adding words to a picture), you’ll need to use the CGContext text options.

The majority of the text-related functions modify the graphical state, as described in table 13.13. The last two functions in the table draw your text.

Table 13.13. A variety of functions for drawing text in Quartz

Function

Arguments

Summary

CGContextSelectFont context, font name, size, text encoding Sets a font for the graphical state
CGContextSetTextDrawingMode context, CGTextDrawingMode Defines how to draw text in the graphical state
CGContextSetTextMatrix context, affine transform Places a transformation matrix in the graphical state for drawing only text
CGContextSetSetPosition context, x, y Sets where to draw in the graphical state
CGContextShowText context, string, length Draws the text at the current position
CGContextShowTextAtPoint context, x, y, string, length Draws the text at the specified position

You can find several other text-related functions in the CGContext reference. Most notably, if you need more control over your fonts (and particularly if you want to link up to UIFonts), you should use CGContextSetFont and CGContextSetFontSize instead of the CGContextSelectFont function that’s noted here—but keep in mind that you can’t use CGContextShowTextAtPoint when you set your font in this alternative way.

Here’s a simple example of printing text in Quartz:

CGContextSelectFont (ctx, "Helvetica",20,kCGEncodingMacRoman);
CGContextSetTextDrawingMode(ctx, kCGTextFill);
CGAffineTransform flip = CGAffineTransformMake(1,0,0,-1,0,0);
CGContextSetTextMatrix(ctx, flip);
CGContextShowTextAtPoint(ctx, 20, 85, "A Quartz Example", 16);

The only thing of note is the creation of the affine transformation matrix, flip. We’ve already pointed out that the text-drawing functions don’t use the iOS coordinate system at present. Instead, they’re stored in an inverted manner, so you need to flip them over to use them correctly. (We hope that this changes in some future release of iOS.)

The affine transformation shown here describes the matrix using the CGAffineTransformMake function. It effectively does the same thing as the two-part transformation in listing 13.1. In our view, it’s a bit simpler but less clear.

That’s only the basics of using text, but it should be enough to get you started when you need to draw in Quartz.

13.5.4. What we didn’t cover

Quartz 2D is a fully featured drawing and painting language that we can only briefly touch on in this chapter. Among the other topics you may want to research if you’re going to do more advanced work with Quartz are patterns, transparency layers, layer drawing, and PDF creation. As we’ve mentioned previously, Apple’s “Quartz 2D Programming Guide” is an excellent introduction to these topics.

We’re not quite finished with Quartz. Before we finish this chapter, we’ll put together an example that combines some of the Quartz lessons from this chapter with some of the photographic work we covered in chapter 11.

13.6. Drawing on a picture: an example

To put together the lessons we’ve covered, you’ll create a program that allows a user to load up a picture, draw on it, and then save the results. Figure 13.5 shows the intended result.

Figure 13.5. PhotoDraw can place drawings on pictures.

As usual, you’ll begin by building your interface visually, but you have only two simple things to do:

  1. Create a UIButtonBar with a single action-type button (which is one of the standard styles you can select for a button).
  2. Link the existing UIView to a new drawView class (which should be a UIView subclass).

When you get into Xcode, the programming will look a lot like the collage program in chapter 11, but with some nuances related to your greater understanding of Quartz.

You’ll do the coding in two parts. The overall structure of the program will go in PhotoDrawViewController.m, and the drawing specifics will go in drawView.m.

13.6.1. The PhotoDraw view controller

The view controller manages an image selector as well as several toolbar buttons, including the action button you created, and Save and Cancel buttons that appear later. The code is shown in listing 13.2. We’ve omitted some of the view controller’s overall structure and focused on the code that’s involved when the user pushes the action button and activates choosePic:.

Listing 13.2. The important bits of a view controller for a PhotoDraw program

This is a fairly simple snippet of code because it shows the view controller acting as a traffic cop, accepting input from controls and sending messages to other objects, which is pretty much the definition of what a view controller should do.

For once, you don’t have any setup in viewDidLoad:. Instead, the toolbar initiates your program’s actions. At startup, the user has only one choice: to click the action button and start the image picker. When the picker returns, you modify the UIButtonBar to give options for Save and Cancel, and then you send the picture to drawView to be dealt with . Alternatively, you clear away the image picker if the user cancels it .

The save-picture routine works the same way as the one you wrote in the collage program. The only difference is that this one includes a callback, which ends the program after the saving is done . The clear-drawing method, meanwhile, makes a call to the drawView object again.

To learn what’s done with the initial picture, how drawing occurs, and what happens when the drawing is cleared, we need to look at this program’s other major class.

13.6.2. The photodraw view

As you saw in the previous section, the view controller hands off three responsibilities to the view: displaying a picture, responding to touch events, and clearing the drawing. We’ll step through these functions one at a time.

Here’s what’s done when a user picks an image:

-(void)drawPic:(UIImage *)thisPic {
    myPic = thisPic;
    [myPic retain];
    [self setNeedsDisplay];
}

This routine is simple: it saves the picture to an instance variable and then alerts the UIView that its CALayer must be drawn.

We’ll save the CALayer’s drawRect: method for last, so we’ll look now at how the drawView class interprets touch events. This is shown in the next listing.

Listing 13.3. Recording touch events
- (void) touchesBegan:(NSSet *)
    touches withEvent:(UIEvent *)event {
    [myDrawing addObject:[[NSMutableArray alloc] initWithCapacity:4]];
     CGPoint curPoint = [[touches anyObject] locationInView:self];
     [[myDrawing lastObject] addObject:[NSNumber
         numberWithFloat:curPoint.x]];
     [[myDrawing lastObject] addObject:[NSNumber
         numberWithFloat:curPoint.y]];
}
- (void) touchesMoved:(NSSet *)touches
    withEvent:(UIEvent *)event {
     CGPoint curPoint = [[touches anyObject] locationInView:self];
     [[myDrawing lastObject] addObject:[NSNumber
         numberWithFloat:curPoint.x]];
     [[myDrawing lastObject] addObject:[NSNumber
         numberWithFloat:curPoint.y]];
     [self setNeedsDisplay];
}
- (void) touchesEnded:(NSSet *)touches
    withEvent:(UIEvent *)event {

     CGPoint curPoint = [[touches anyObject] locationInView:self];
     [[myDrawing lastObject] addObject:[NSNumber
         numberWithFloat:curPoint.x]];
     [[myDrawing lastObject] addObject:[NSNumber
         numberWithFloat:curPoint.y]];
     [self setNeedsDisplay];
}

The overall concept here is simple. You maintain an NSMutableArray called myDrawing as an instance variable. Within that, you create a number of NSMutable-Array subarrays, each of which contains an individual path. You set up a new subarray when a touch starts and then add the current point when the touch moves or ends. The result is an array that contains a complete listing of all touches. But again, you’ll have to wait to see how that’s drawn.

It’s notable that you tell drawView to draw (via the setNeedsDisplay method) both when a touch moves and when it ends. That’s because whenever the touch moves, you want to provide instant gratification by drawing what the user has sketched out so far. When the touch ends, you do the same thing.

The following method clears all current drawings. Its functionality is obvious now that you know that the list of drawings is held as an array:

-(void)cancelDrawing {

    [myDrawing removeAllObjects];
    [self setNeedsDisplay];
}

At this point, the drawView object is maintaining two different instance variables: myPic contains the current picture, and myDrawing contains an array of paths. Putting them together into a coherent whole requires using some of the Quartz functions we discussed in the last two chapters. The results are shown in the next listing.

Listing 13.4. Drawing from user-created variables

The bulk of this method is spent iterating through the information you saved in other methods. Four Quartz functions do the drawing work. First, you draw the selected image. You go back to using the UIKit methods from chapter 11 so the image doesn’t end up upside-down. Then, you begin working through the myDrawing array. Each subarray results in your program beginning a new path and moving to the start. As you move through the array, you add lines . Finally, when a subarray is complete, you stroke the path .

The result allows for drawing simple lines on a picture, which can then be saved, as you saw back in the view controller.

But is it possible to do more with this example? As usual, the answer is, yes.

13.6.3. Expanding on the example

If you want to expand this example into a more complete application, you can take several routes. The first and most obvious expansion is to select a color before drawing a line. The hard part is creating a color picker, although you can make a standalone class that you can then reuse elsewhere. With that in hand, it’s simple to add a color variable to your line arrays, by always saving it as the 0 element of a subarray.

The program could also benefit from a more sophisticated line-drawing algorithm that tosses out nearby points and smoothes the lines into curves, removing some of the sharp edges that show up in the current program.

In any case, that ends our look at Quartz 2D. There’s a lot more you can learn, but you should have the foundation you need to move forward.

Two other ways you can draw using the SDK are Core Animation and OpenGL. We don’t have the space in this introductory book to give full attention to either, but we’ll introduce them and show you where to go for more information, beginning with Core Animation.

13.7. An introduction to Core Animation

Core Animation is a fundamental technology on the iPhone and iPad. It manages all the nifty scrolls, pivots, zoom-ins, zoom-outs, and other bits of animation that make up the user interface. As you’ve already seen, many UIKit classes give you an option to use animation or not, usually by having an animated: argument as part of a method.

Core Animation is also tightly integrated with Quartz. As you’ve seen, each UIView is linked to a graphical layer called the CALayer, which is the Core Animation layer. Though you’ve only used it to depict simple graphics and images so far, you can also use it to manage more complex changes.

But you don’t have to use Quartz at all to create animations. There’s a CALayer behind every UIView; and because almost everything is built on a UIView, you can animate your existing UIViews, possibly including pictures that you’ve loaded into UIImageViews. For example, figure 13.6 shows how you can use Core Animation to show an approaching plane by moving its UIImageView and turning it opaque as it approaches.

Figure 13.6. A jet moves across the screen on an iPhone, thanks to Core Animation.

This is the example we’ll show later in this section, using two different means to create the animation.

13.7.1. The fundamentals of Core Animation

When we speak of animation using Core Animation, we’re talking about changing the properties of the CALayer and then smoothly animating those property changes. The CALayer class reference lists which properties can be animated; they include anchor-Point, backgroundColor, opacity, position, transform, and several others. This means you can use Core Animation to animate the position of an object, its color, its transparency, and also its CGAffine transformations.

Before we get further into Core Animation, we want to talk about its fundamentals—those terms and ideas that you’ll meet throughout this section:

  • Layer—This is where animation occurs. You always have one CALayer hooked up to every UIView, accessible via the layer property. You can call up additional layers with a [CALayer layer] class message and then add them to your existing CALayer with the addSublayer: method. Adding layers this way results in inverted coordinate systems. Each layer can be individually animated, allowing for complex interactions between numerous animated properties. You may find it as easy to create a more complex animation by creating multiple UIKit objects (most likely multiple UIImageViews) and animating each one.
  • Implicit animation—This is the simplest type of animation. You tell the UIView that it should animate and then you change the properties.
  • Explicit animation—This is an animation created with CABasicAnimation that allows you to more explicitly define how the property change animates.
  • Key-frame animation—This is an even more explicit type of animation, where you define not only the start and end of the animation but also some of the frames in between.

You can also create much more complex animations, such as redefining how implicit animations work, collecting animations into transactions, and building complex animation layer hierarchies. For more information, look at the “Core Animation Programming Guide” and the “Core Animation Cookbook,” both available from Apple.

13.7.2. Getting started with Core Animation

To use Core Animation, make sure you add Quartz Core, the framework required for animation, to your project. You should also include QuartzCore/QuartzCore.h, the main header file for Core Animation.

With that done, you’re ready to try the two simplest types of animation: a simple implicit animation and an explicit animation.

13.7.3. Drawing a simple implicit animation

Implicit animations are the simplest type of animation, because they just require starting an animation block and then changing CALayer-level properties. The following code shows a simple example involving a UIImageView called plane that contains a clipart picture of a plane. The image starts at the upper-left corner of the screen with 25 percent opacity and moves downward while growing more opaque:

[UIView beginAnimations:nil context:NULL];
CGAffineTransform moveTransform
    = CGAffineTransformMakeTranslation(200, 200);
[plane.layer setAffineTransform:moveTransform];
plane.layer.opacity = 1;
[UIView commitAnimations];

Between them, beginAnimations:context: and commitAnimations define an animation block.

Within the block, you set two properties to animate. setAffineTransform: is a special CALayer method that allows the setting of its transform property using an affine transformation matrix, which you’re already familiar with; opacity is a more obvious property.

Alternatively, you can pass the implicit animation blocks in class method. The following table contains common animations API in iOS 4:

Table 13.2. Common animations API with blocks arguments in iOS 4

Class method

Details

+ (void)animateWithDuration:
(NSTimeInterval)duration animations:
(void (ˆ)(void))animations
This method will apply the animation blocks to views without delay and the duration is in seconds.
+ (void)animateWithDuration:
(NSTimeInterval)duration animations:
(void (ˆ)(void))animations completion:
(void (ˆ)(BOOL finished))completion
This method will apply the animation blocks to views and execute the completion handler after the animation.
+ (void)animateWithDuration:
(NSTimeInterval)duration delay:
(NSTimeInterval)delay options:
(UIViewAnimationOptions)options
animations:(void (ˆ)(void))animations completion:
(void (ˆ)(BOOL finished))completion
This method will apply the animation blocks, options and completion handler to the views. UIViewAnimationOptions options can be defined.
+ (void)transitionWithView:
(UIView *)view duration:
(NSTimeInterval)duration options:
(UIViewAnimationOptions)options animations:
(void (ˆ)(void))animations completion:
(void (ˆ)(BOOL finished))completion
This method will apply the animation transition from the first view to the second view. For example, the view controller can use this method to transit from the main view to the flip view.

Let’s take a look at the previous code example, the plane image starts at the upper-left corner of the screen with 25 percent opacity and moves downward while growing more opaque. Use the animation blocks API, use the default animation duration value, 0.2 second.

[UIView animateWithDuration:0.2 animations:ˆ{
 CGAffineTransform moveTransform = CGAffineTransformMakeTranslation(200,200);
 [plane.layer setAffineTransform:moveTransform];
 plane.layer.opacity = 1;
}];

With a start of “ˆ”, the complier understands the following anonymous blocks of code applying to animations.

As soon as you close out the block, the animation begins. The plane moves and grows more distinct. That’s all there is to it!

But sometimes an implicit animation doesn’t give you as much control as you want. That’s where explicit animations come in.

13.7.4. Drawing a simple explicit animation

When you’re working with explicit animations, instead of defining a bunch of changes to a CALayer and executing them all, you define animations one by one using the CABasicAnimation class. Each of these animations can have its own value for duration, repeatCount, and numerous other properties. You then apply each animation to a layer separately, using the addAnimation:forKey: method.

The following code executes an animation similar to the previous one but with more control:

CABasicAnimation *opAnim = [CABasicAnimation
    animationWithKeyPath:@"opacity"];
opAnim.duration = 3.0;
opAnim.fromValue = [NSNumber numberWithFloat:.25];
opAnim.toValue= [NSNumber numberWithFloat:1.0];
opAnim.cumulative = YES;
opAnim.repeatCount = 2;
[plane.layer addAnimation:opAnim forKey:@"animateOpacity"];
CGAffineTransform moveTransform
    = CGAffineTransformMakeTranslation(200, 200);
CABasicAnimation *moveAnim = [CABasicAnimation
    animationWithKeyPath:@"transform"];
moveAnim.duration = 6.0;
moveAnim.toValue= [NSValue valueWithCATransform3D:
    CATransform3DMakeAffineTransform(moveTransform)];
[plane.layer addAnimation:moveAnim forKey:@"animateTransform"];

This example is definitely longer than the implicit animation example, but you get to define the two animations with separate durations, which is the first step to creating a more beautiful and better-controlled animation. Note that you also use yet another way to change an affine transformation matrix into a Transform3D matrix of the type used by Core Animation: the CATransform3DMakeAffineTransform function.

The code includes a bit of a kludge: to keep the plane opaque through the last 3 seconds, it keeps counting opacity cumulatively, making it climb from 1.0 to 1.75 the second time through. A better solution would create three key frames for opacity: .25 at 0 seconds, 1.0 at 3 seconds, and 1.0 at 6 seconds. That’s why you may want to use a key-frame animation of the sort we alluded to at the start of this section, rather than a basic animation.

These simple methods for using Core Animation can take you far. Look through the CALayer class reference for everything you’re allowed to animate. For more details, read the two Apple guides we pointed out.

Before we leave graphics behind, we want to touch on one other toolkit: OpenGL.

13.8. An introduction to OpenGL

OpenGL is SGI’s standardized 2D and 3D graphical drawing language. The iPhone and iPad more specifically use OpenGL ES, or OpenGL for Embedded Systems, which features a reduced API for use on devices like mobile phones. For full information about using OpenGL, you should pick up a book on the topic or read Apple’s “OpenGL ES Framework Reference,” which links to the most important documents available from Apple. We’ll cover some of the general information you need to access OpenGL through iOS.

iOS manages OpenGL through EAGL, a class that interfaces between the device’s views and OpenGL’s drawing functions. It allows for the writing of OpenGL functions onto an EAGLView, which is the CAEAGL layer of a UIView, showing the same layer-based paradigm you met when using Core Animation.

To simplify your programming of OpenGL projects, Xcode supplies a standard template to use, which sets up all the OpenGL defaults for you. It’s the OpenGL ES Application template, the only Xcode template that we have yet to examine. This template includes all the basic setup of OpenGL, which is extensive. That includes the setup of a timer, the creation of frame buffers, and the code needed to draw something. To do basic OpenGL programming, all you have to do is write your code into the drawView method of the EAGLView class.

Rather than giving a completely insufficient overview of this enormous library, we’ll instead point you toward a few bits of sample code. The OpenGL template comes complete with a rotating square as an example. There are also three OpenGL samples currently available from Apple: GLGravity shows simple OpenGL rendering related to accelerometer output, GLSprite demonstrates texturing, and GLPaint explores another way to allow finger painting.

These examples should be sufficient to get you started if you already have a strong basis in OpenGL and need to see how it’s integrated into the iPhone and iPad.

13.9. Summary

Graphics are one of the most important elements for making your projects look great. Not only does iOS support high-quality graphics, but it also gives you a wide variety of options, depending on the needs of your program.

Quartz 2D will be your main workhorse for most graphical programs. If you’re already familiar with the Canvas library for the web, you’ll see that Quartz is similar. You can draw paths and use many graphical state variables to modify exactly how that path is painted.

Core Animation is an expansion to Quartz that was created for the iPhone and iPad. You’ve already seen it integrated into numerous native programs, and now you can use it yourself. Core Animation is built around the idea of automated animations: you tell it the endpoints, and Core Animation fills in the rest for you. Again, this is much as you may have seen on the web, with the WebKit’s various styles of implicit and explicit animation.

OpenGL is a whole new graphics library that has been imported into iOS, much as SQLite is a third-party library that Apple made available to developers. The difference is that Apple has made OpenGL easier to use, thanks to the creation of the EAGL framework. Although this chapter suggests how to get started with OpenGL, the topic is large enough that you’ll need to pick up a book to fully explore the topic.

With graphics covered, we need to look at another major topic in the SDK toolkit: the internet. How do you access the Net, and how do you use various protocols that let you access the web’s ever-growing social network?

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.50.222