Taking Pictures and Movies

Taking a picture or movie with an image picker controller is a three part process that proceeds as an interplay between your code and the system:

  1. You instantiate and modally present a camera interface—an instance of the UIImagePickerController class.

  2. The system manages the camera interface and the user’s interaction with it. In typical use, the user either takes a picture or movie, or cancels the operation.

  3. The system invokes your image picker controller delegate object’s methods, which in turn handle the results of the user’s interaction—for example, by saving a new picture to the Camera Roll album. The delegate is also responsible for dismissing the camera interface.

A default image picker controller includes a variety of features, as shown in Figure 1.

Figure 1  An image picker controller

This chapter explains how to use a default image picker controller and delegate for taking pictures and movies. (Movie recording is available starting in iOS 3.0 on supported devices).

To learn how to instead use the AV Foundation framework for fully-customized media capture, see “Media Capture and Access to Camera” in AV Foundation Programming Guide.

Creating and Configuring a Camera Interface

To present a camera interface, you must first ensure that three things are in place:

  1. The device your app is running on must have a camera.

    If taking pictures or movies is essential to your app, specify that by configuring the UIRequiredDeviceCapabilities key in your app’s Info.plist property list file. See “UIRequiredDeviceCapabilities” in Information Property List Key Reference for the various camera characteristics you can specify as required.

    If capturing media is incidental to your app—that is, if your app remains useful even if the device doesn’t have a camera—then your code should follow an alternate path when running on a device without a camera.

  2. The device’s camera must be available for you to use, which you can test by way of the isSourceTypeAvailable: class method of the UIImagePickerController class.

  3. You must have implemented a delegate object to respond to the user’s interaction with the image picker controller. (See “Implementing a Delegate for the Camera Interface.”)

With those prerequisites satisfied, create and then configure an image picker controller by specifying the following options:

Listing 1 verifies the prerequisites are satisfied by way of its method signature and a conditional test, and goes on to instantiate, configure, and asynchronously present the camera user interface full screen.

Listing 1  Presenting the camera interface full screen

- (BOOL) startCameraControllerFromViewController: (UIViewController*) controller
               usingDelegate: (id <UIImagePickerControllerDelegate,
                                   UINavigationControllerDelegate>) delegate {
 
    if (([UIImagePickerController isSourceTypeAvailable:
                 UIImagePickerControllerSourceTypeCamera] == NO)
            || (delegate == nil)
            || (controller == nil))
        return NO;
 
 
    UIImagePickerController *cameraUI = [[UIImagePickerController alloc] init];
    cameraUI.sourceType = UIImagePickerControllerSourceTypeCamera;
 
    // Displays a control that allows the user to choose picture or
    // movie capture, if both are available:
    cameraUI.mediaTypes =
        [UIImagePickerController availableMediaTypesForSourceType:
            UIImagePickerControllerSourceTypeCamera];
 
    // Hides the controls for moving & scaling pictures, or for
    // trimming movies. To instead show the controls, use YES.
    cameraUI.allowsEditing = NO;
 
    cameraUI.delegate = delegate;
 
    [controller presentModalViewController: cameraUI animated: YES];
    return YES;
}

Listing 1 provides a picker that lets the user capture still images and movies, if both are available on the device. To instead present a picker that captures only movies, for example, ensure that movie capture is available and then set the mediaTypes property as follows:

cameraUI.mediaTypes = [[NSArray alloc] initWithObjects: (NSString *) kUTTypeMovie, nil];

(To ensure that movie capture is available, call the availableMediaTypesForSourceType: class method.)

For a picker that captures only still images, replace the kUTTypeMovie identifier here with kUTTypeImage, or rely on the default value of the mediaTypes property, which is kUTTypeImage.

The startCameraControllerFromViewController:usingDelegate: example method from Listing 1 is designed to be invoked by an action method such as this:

- (IBAction) showCameraUI {
    [self startCameraControllerFromViewController: self
                                    usingDelegate: self];
}

Notice, as specified in the method signature in Listing 1, that the delegate object must conform to the UIImagePickerControllerDelegate and UINavigationControllerDelegate protocols.

The result of the startCameraControllerFromViewController:usingDelegate: example method is that the system modally displays the standard camera interface, including controls for capturing media or canceling, as shown in Figure 1.

Implementing a Delegate for the Camera Interface

When the user taps a button in the camera interface to accept a newly captured picture or movie, or to just cancel the operation, the system notifies the delegate of the user’s choice. The system does not, however, dismiss the camera interface. The delegate must dismiss it by calling the dismissModalViewControllerAnimated: method and must release the interface as well. It is for this reason that, typically, you make the view controller that presents the camera interface serve double-duty as the delegate.

Listing 2 shows example implementations of delegate methods for an image picker controller. The imagePickerController:didFinishPickingMediaWithInfo: implementation includes code for saving a still image or a movie, depending on what the user captured.

Listing 2  Delegate methods for a camera interface

@implementation CameraViewController (CameraDelegateMethods)
 
// For responding to the user tapping Cancel.
- (void) imagePickerControllerDidCancel: (UIImagePickerController *) picker {
 
    [[picker parentViewController] dismissModalViewControllerAnimated: YES];
    [picker release];
}
 
// For responding to the user accepting a newly-captured picture or movie
- (void) imagePickerController: (UIImagePickerController *) picker
            didFinishPickingMediaWithInfo: (NSDictionary *) info {
 
    NSString *mediaType = [info objectForKey: UIImagePickerControllerMediaType];
    UIImage *originalImage, *editedImage, *imageToSave;
 
    // Handle a still image capture
    if (CFStringCompare ((CFStringRef) mediaType, kUTTypeImage, 0)
            == kCFCompareEqualTo) {
 
        editedImage = (UIImage *) [info objectForKey:
                    UIImagePickerControllerEditedImage];
        originalImage = (UIImage *) [info objectForKey:
                    UIImagePickerControllerOriginalImage];
 
        if (editedImage) {
            imageToSave = editedImage;
        } else {
            imageToSave = originalImage;
        }
 
    // Save the new image (original or edited) to the Camera Roll
        UIImageWriteToSavedPhotosAlbum (imageToSave, nil, nil , nil);
    }
 
    // Handle a movie capture
    if (CFStringCompare ((CFStringRef) mediaType, kUTTypeMovie, 0)
            == kCFCompareEqualTo) {
 
        NSString *moviePath = [[info objectForKey:
                    UIImagePickerControllerMediaURL] path];
 
        if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum (moviePath)) {
            UISaveVideoAtPathToSavedPhotosAlbum (
                    moviePath, nil, nil, nil);
        }
    }
 
    [[picker parentViewController] dismissModalViewControllerAnimated: YES];
    [picker release];
}
 
@end

If image editing is enabled and the user successfully accepts a newly captured picture, the info parameter of the imagePickerController:didFinishPickingMediaWithInfo: method contains a dictionary that includes the edited image. Treat this image as the selected image, as done in Listing 2. If you want to store the original image, you can get it from the dictionary, also as shown in the code listing.

Instead of immediately saving the new media item to the user’s Camera Roll as shown in this example, you could instead call custom code to work with the media.