344
Media:
images and sounds
So far, our focus on iPhone programming has mainly been on text. Sure, we’ve dis-
played the occasional
UIImage
, such as the mountain drawing in the last chapter,
but we’ve only considered the simplest means for doing so.
The iPhone offers an experience that’s potentially much richer and more
engaging. A camera, a microphone, a complete photos library, and a speaker are
just some of the utilities built into the iPhone. In this chapter, we’re going to look
at these features as part of a general exploration of media. We’ll provide deep cov-
erage on images, some information on the iPhone’s media player, and a basic look
at playing sounds on the iPhone.
More complex questions are beyond the scope of this chapter. We’re saving the
topic of image editing for the next chapter, when we look at the iPhone’s graphic
libraries. For more complex sound work, we’ll offer pointers to Apple’s extensive
tutorials on the topic.
This chapter covers
■
Accessing and manipulating images
■
Using the iPhone camera
■
Playing sounds and video
345An introduction to images
18.1 An introduction to images
We’ve already touched upon using images a few times, beginning in chapter 12 where
we included an image in one of our earliest
SDK
examples. We’ve always created
a
UIImageView
in Interface Builder, attached it to a filename, and not worried about
the details.
We’re now ready to consider the details. We’ll look
at some of the options you have available when you
dive into Xcode, rather than depending upon Inter-
face Builder’s higher-level abstractions.
When you look more closely, you’ll discover that
using images is a two-step process. First, you load data
into a
UIImage
, and then you make use of that
UIImage
via some other means. There are two major ways to
make use of
UIImage
s, as shown in figure 18.1.
We’re going to explore the simpler methods of dis-
playing images, using
UIImageView
, in this section,
and in section 18.2 we’ll examine the more complex
means available for drawing images onto the back
layer of a
UIView
.
18.1.1 Loading a UIImage
The
UIImage
class offers seven different ways to create an instance of an image. The
four factory methods are probably the easiest to use, and they’re the ones we’ve listed
in table 18.1. There are also some equivalent init methods that you can use if you prefer.
The image data can be of several file types, including
BMP
,
CUR
,
GIF
,
JPEG
,
PNG
, and
TIFF
.
In this book, we’ve used mostly
JPEG
s (because they’re small) and
PNG
s (because they
look good and are accelerated on the iPhone hardware). You can also create a
UIImage
from a Quartz
2D
object; this is the iPhone’s fundamental graphics package, which we’re
going to talk about more in the next chapter. There’s one suggested restriction when
you’re creating
UIImage
s: the images shouldn’t be larger than 1024x1024.
Table 18.1 Factory methods for creating a
UIImage
Factory method Summary
imageNamed:
Creates a
UIImage
based on a file in the main bundle
imageWithCGImage:
Creates a
UIImage
from a Quartz 2D object; this is
the same as
initWithCGImage:
imageWithContentsOfFile:
Creates a
UIImage
from a complete file path that you
specify, as discussed in chapter 16; this is the same
as
initWithContentsOfFile:
imageWithData:
Creates a
UIImage
from
NSData
; this is the same
as
initWithData:
CALayer
Data
UIImage
UIImageView UIView
Figure 18.1 Images can be shown
in
UIImageView
s or in
UIView
s.
346
C
HAPTER
18
Media: images and sounds
Once you import an image into your program, you can display it. If you’re going to
stay entirely within the simple methods of the
UIKit
, you’ll want to use the
UIImage-
View
class to display the image.
18.1.2 Drawing a UIImageView
We’ve already used the
UIImageView
in our programs when displaying pictures. We’re
now ready to talk about the details of how it works.
There are two ways to initialize a
UIImageView
. First, you can use the
initWith-
Image:
method, which allows you to pass a
UIImage
, as follows:
UIImage *myImage1 = [UIImage imageNamed:@"sproul1.jpg"];
UIImageView *myImageView =
[[UIImageView alloc] initWithImage:myImage1];
[self.view addSubview:myImageView];
Alternatively, you can use a plain
initWithFrame:
method and modify the object’s
properties by hand. Table 18.2 shows a few of the properties and methods that you’re
most likely to use when doing more extensive work with a
UIImageView
.
To load a normal image, you could use the
image
property, but there’s usually little
reason to use it rather than the
initWithImage:
method—unless you’re dynamically
changing your image. If you want to create a set of images to animate, it’s useful to
take advantage of the other
UIImageView
methods and properties.
You can load an array of images into a
UIImageView
, declare how fast and how
often they should animate, and start and stop them as you see fit. A simple example of
this is shown in listing 18.1.
- (void)viewDidLoad {
UIImage *myImage1 =
[UIImage imageNamed:@"sproul1.jpg"];
UIImage *myImage2 =
Table 18.2 A few properties and methods of note for
UIImageView
Method or property Type Summary
animationDuration
Property Specifies how often an animation cycles
animationImages
Property
Identifies an
NSArray
of images to load into the
UIImageView
animationRepeatCount
Property Specifies how many times to run an animation cycle
image
Property
Identifies a single image to load into a
UIImageView
startAnimating
Method Starts the animation
stopAnimating
Method Stops the animation
Listing 18.1
UIImageView
allows for animated images
Loads images
347Drawing simple images with Core Graphics
[UIImage imageNamed:@"sproul2.jpg"];
UIImage *myImage3 =
[UIImage imageNamed:@"sproul3.jpg"];
UIImage *myImage4 =
[UIImage imageNamed:@"sproul4.jpg"];
UIImageView *myImageView =
[[UIImageView alloc]
initWithFrame:[[UIScreen
mainScreen] bounds]];
myImageView.animationImages =
[NSArray arrayWithObjects:myImage1,
myImage2,myImage3,myImage4,nil];
myImageView.animationDuration = 4;
[myImageView startAnimating];
[self.view addSubview:myImageView];
[myImageView release];
[super viewDidLoad];
}
Taking advantage of
UIImageView
’s animation capability is one of the main reasons
that you might want to load images by hand rather than do it through Interface
Builder.
18.1.3 Modifying an image in the UIKit
Now you’ve seen how to create images and load them into image views programmati-
cally. Surely, the next thing to do is to start modifying them.
Unfortunately, you have only limited capability to do so while working with
UIImageView
. You can make some changes, based on simple manipulations of the view.
For example, if you resize your
UIImageView
, it’ll automatically resize the picture it
contains. Likewise, you can decide where to draw your
UIImageView
by setting its
frame to something other than the whole screen. You can even layer multiple images
by using multiple
UIImageView
s.
This all starts to get unwieldy pretty quickly, though, and you can’t do anything fan-
cier, like transforming your images or modifying how they stack through blending or
alpha transparency options. To do that sort of work (and to start stacking graphics,
not just views) you need to learn about Core Graphics.
UIImage
offers some simple ways to access Core Graphics functionality that doesn’t
require going out to the Core Graphics framework (or learning about contexts or the
other complexities that underlie its use). We’re going to talk about those briefly here,
but, for the most part, Core Graphics will wait for the next chapter, which concen-
trates on the entire Quartz
2D
graphics engine.
18.2 Drawing simple images with Core Graphics
Although it doesn’t give access to the entire Core Graphics library of transformations
and other complexities, the
UIImage
class does include five simple methods that take
advantage of the way Core Graphics works. They’re described in table 18.3.
Loads images
Creates
UIView
Starts
animation
348
C
HAPTER
18
Media: images and sounds
The trick is that these methods cannot be used as part of
viewDidLoad:
or whatever
other method you usually use to load up your objects. That’s because they depend
upon a graphical context to work. We’re going to talk about contexts more in the next
chapter, but a graphical context is a destination that you’re drawing to, like a window,
a
PDF
file, or a printer.
On the iPhone,
UIView
s automatically create a graphical context as part of their
CALayer
, which is a Core Animation layer associated with each
UIView
. You can access
this layer by writing a
drawRect:
method for the
UIView
(or rather, for a new subclass
that you’ve created). You’d usually have to capture a special context variable to do this
type of work, but the
UIView
methods take care of this for you, to keep things simple.
Listing 18.2 shows how to collage together a few pictures using this method.
- (void)drawRect:(CGRect)rect {
UIImage *myImage1 = [UIImage imageNamed:@"sproul1.jpg"];
UIImage *myImage2 = [UIImage imageNamed:@"sproul2.jpg"];
UIImage *myImage3 = [UIImage imageNamed:@"sproul3.jpg"];
[myImage1 drawAtPoint:CGPointMake(0,0) blendMode:kCGBlendModeNormal
alpha:.5];
[myImage2 drawInRect:CGRectMake(10, 10, 140, 210)];
[myImage3 drawInRect:CGRectMake(170, 240, 140, 210)];
}
Note that the
drawAtPoint:
method gives you access to more complex possibilities,
such as blending your pictures (using Photoshop-like options such as color dodge and
hard light) and making them partially transparent. Here you’re using a normal blend,
but only 50 percent transparency (hence the use of the
drawAtPoint:
method). The
rest of the code is standard enough. The simplicity of using these singular draw com-
mands rather than going to the effort of creating multiple
UIImageView
objects speaks
for itself (and it’s presumably more efficient too).
There’s still a lot that we can’t do until we delve fully into the iPhone’s Core Graph-
ics framework, but for now we’ve got some control, which should be sufficient for
Table 18.3 Instance methods for drawing a
UIImage
Method Summary
drawAsPatternInRect:
Draws the image inside the rectangle,
unscaled, but tiled as necessary
drawAtPoint:
Draws the complete unscaled image with
the
CGPoint
as the top-left corner
drawAtPoint:blendMode:alpha:
A more complex form of
drawAtPoint:
drawInRect:
Draws the complete image inside the
CGRect
, scaled appropriately
drawInRect:blendMode:alpha:
A more complex form of
drawInRect:
Listing 18.2 A
UIView
’s
drawRect:
allows you to use lower-level draw commands
349Accessing photos
most of your common media needs. If you need more control, skip right ahead to the
next chapter.
We’ve talked lots about images, and we’ve pre-
sumed so far that you’re loading them from your proj-
ect’s bundle. But what if you want to let a user select
photographs? That’s the topic of our next section.
18.3 Accessing photos
You can use the
SDK
to access pictures from an
iPhone’s photo library or its camera roll. You can also
allow a user to take new photos. This is all done with
the
UIImage-PickerController
, another modal con-
troller that manages a fairly complex graphical inter-
face without much effort on your part. Figure 18.2
shows what it looks like.
18.3.1 Using the image picker
The
UIImagePickerController
is loaded up by creat-
ing the object, setting a few variables, and presenting it
as a modal view controller. By default, the image picker
controller will allow users to access (and optionally
edit) the pictures in their photo library:
UIImagePickerController *myImagePicker =
[[UIImagePickerController alloc] init];
myImagePicker.delegate = self;
myImagePicker.allowsImageEditing = NO;
[self presentModalViewController:myImagePicker animated:YES];
Once you’ve created your image picker controller, you need to have its delegate
respond to two methods:
imagePickerController:didFinishPickingImage:edit-
ingInfo:
and
imagePickerControllerDidCancel:
. For the first method, you should
dismiss the modal view controller and respond appropriately to the user’s picture
selection, and for the second, you only need to dismiss the controller.
Overall, the image picker controller is easy to use because you’re mainly reacting to
a picture that was selected. We’ve got a complete example of its use in the next section.
18.3.2 Taking photos
As we noted earlier, the
UIImagePickerController
has three possible sources, repre-
sented by these constants:
■
UIImagePickerControllerSourceTypePhotoLibrary
, a picture from the photo
library
■
UIImagePickerControllerSourceTypeSavedPhotosAlbum
, a picture from the
camera roll
■
UIImagePickerControllerSourceTypeCamera
, new picture taken by the camera
Figure 18.2 The image picker
is another preprogrammed
controller for your use.
350
C
HAPTER
18
Media: images and sounds
You should always make sure that the source is available before you launch an image
picker controller, although this is most important for the camera. You can confirm
that the source exists with the
isSourceTypeAvailable:
class method:
if ([UIImagePickerController
isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
Once you’ve verified the existence of a source, you can tell the image picker to use it
with the
sourceType
property. For example, to use the camera, do the following:
myImagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
Note that pictures taken in a program only go to that program. If you want them to go
into the photo album, your program will have to save them there (as we’ll discuss
momentarily).
In our experience, the camera is a bit of a resource hog. We had it grind to a halt a
few times during testing. More than anything else, this means that you need to think
about saving your program’s state when using the camera, because it could cause you
to run out of memory.
We’ll have an example of using the camera in our example in section 18.4.
18.3.3 Saving to the photo album
You may wish to save a new photograph to the photo album, or you may wish to place
a graphic created by your program there. In either case, you use the
UIImageWriteTo-
SavedPhotosAlbum
function. It has four variables: the first lists the image, and the
other three reference an optional asynchronous notification function to call when the
save has been completed. Usually you’ll call the function like this:
UIImageWriteToSavedPhotosAlbum(yourImage,nil,nil,nil);
If you instead want to take advantage of the asynchronous notification, take a look at
the
UIKit
function reference, which is where this function is hidden away, or look at
our example in the next chapter.
You can use this function (and a bit of trickery) to save the
CALayer
of a
UIView
to
your photo album, which, for example, will allow you to save those draw commands
that you wrote straight to the
CALayer
earlier. This once more depends upon graphi-
cal contexts, which we’ll explain in the next chapter, but here’s how to do it:
UIGraphicsBeginImageContext(myView.bounds.size);
[myView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *collageImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageWriteToSavedPhotosAlbum(collageImage,nil,nil,nil);
In order for this to work correctly, you must link in the Quartz Core framework.
With all of the fundamentals of images now covered, we’re ready to put them
together in our “big” example for this chapter, which is a program that collages
together multiple pictures, first selecting them with a
UIImagePickerController
,
then allowing them to be moved about with a
UIImageView
, and finally drawing them
to a
CALayer
that can be saved.
351Collage: an image example
18.4 Collage: an image example
The collage program depends on three objects. The
collageViewController
, as
usual, does most of the work. It writes out to a
collageView
object, which exists
mainly as a
CALayer
to be written upon. Finally, you’ll have a
tempImageView
object
that allows the user to position an image after it’s been selected but before it’s perma-
nently placed.
18.4.1 The collage view controller
The collage view controller is built on a few Interface Builder objects: the view con-
troller itself; a toolbar called
myTools
, which will be filled over the course of the pro-
gram; and the
collageView
UIView
class, which exists as its own class file and is
referred to in the program as
self.view
. You’ll also need to add the Quartz Core
framework to your project as you’ll use that save-picture trick that we just discussed.
Listing 18.3 shows the complete view controller, which is the most extensive file in
this program.
@implementation collageViewController
- (void)viewDidLoad {
UIBarButtonItem *picButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self
action:@selector(choosePic:)];
UIBarButtonItem *camButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemCamera target:self
action:@selector(takePic:)];
UIBarButtonItem *saveButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemSave target:self
action:@selector(savePic:)];
picButton.style = UIBarButtonItemStyleBordered;
camButton.style = UIBarButtonItemStyleBordered;
if ([UIImagePickerController
isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
origToolbar = [[NSArray alloc] initWithObjects:
picButton,camButton,saveButton,nil];
} else if ([UIImagePickerController
isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
origToolbar = [[NSArray alloc] initWithObjects:
picButton,saveButton,nil];
} else {
exit(0);
}
[myTools setItems:origToolbar animated:NO];
[picButton release];
[camButton release];
[super viewDidLoad];
}
-(IBAction)choosePic:(id)sender {
Listing 18.3 A view controller manages most of the collage’s tasks
B
Sets up objects
Activates
image picker
C