Cameras Take Pictures

The title of this post is very underwhelming, I know.  But it’s true.  Cameras take pictures.

That you already knew.  And it seems that everyone also knows how to use their trusty Nikon, but since having written the post on how to create your own iOS App, I’ve been asked how did I use the camera on the phone for the SitterSat app?  Here’s how, let me walk you through it.  It’s delightfully easy.

scott sappenfield

Kangaroo lounging around (makes for a nice profile pic)

The Setup

Add a button to your View.  I called mine ProfilePictureButton synthesized to _profilePictureButton (that’s my preferred naming convention in Objective-C).  Why a button?  You can use setImage on a UIButton which will give you all the nice and useful features of a button while at the same time show your photo.

//in your header

@property (weak, nonatomic) IBOutlet UIButton *ProfilePictureButton;

//in your implementation

@synthesize ProfilePictureButton = _profilePictureButton;

//don't forget to unload it - I'm using automatic reference counting (ARC) so no need for me to retain/release

[self setProfilePictureButton:nil];
scott sappenfield

UIButton uses setImage to display your photo

The Implementation

1. Add an implementation method that actually does something.  Remember, I created a button, so let’s associate a method to do something when it’s touched by the user.  Glance over the method and I’ll add some detail to the finer points in the next section.

- (IBAction)ProfilePictureButtonClick:(UIButton *)sender {

    //If the device has a camera, allow a choice via an action sheet
    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
        NSLog(@"Device has a camera, will show ActionSheet for a choice");
        UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle: nil
                                                                 delegate: self
                                                        cancelButtonTitle: @"Cancel"
                                                   destructiveButtonTitle: nil
                                                        otherButtonTitles: @"Take Photo",
                                      @"Choose Existing Photo", nil];
        [actionSheet showFromRect: _profilePictureButton.frame inView: _profilePictureButton.superview animated: YES];
    } else {
        NSLog(@"Device does not have a camera, will show gallery");
        //otherwise, if it doesn't have a camera, just go ahead and show the picker
        UIImagePickerController *picker = [[UIImagePickerController alloc] init];
        picker.delegate = self;
        picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
        picker.allowsEditing = YES;
        [self presentModalViewController:picker animated:YES];
    }

}

2. Ok, so in the ProfilePictureButtonClick method, you’ll see references to UIImagePickerController as well as UIActionSheet.  You’re going to want to tell the compiler about those interfaces, so modify your view controller to include those, as given here with this snippet:

UIViewController <UIImagePickerControllerDelegate, UIActionSheetDelegate>

The reason you need UIActionSheetDelegate is because I chose to use an action sheet when prompting the user.  Specifically, I ask the user with a modal dialog whether or not to use the camera or the gallery on the phone.  You need to ask, if you don’t, get ready for a big rejection on submission to the AppStore. That said, if there is no camera available, you can simply default right to the gallery.  Just think of it a bit like HTML5 modernization techniques where you’re inspecting the devices’ capabilities.

scott sappenfield

Action sheet presents the user with an option

Also notice, for the action sheet, I’ve made the delegate that’s going to handle the user’s responses to the action sheet, self.  Ok, so that’s this view controller.  We’ve already informed the compiler that we’re going to implement methods for this purpose, so let’s do so now.

3. Which button on the action sheet was touched

-(void) actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {

    //Show the picker but only from whatever choice the user made
    UIImagePickerController *picker = [[UIImagePickerController alloc] init];
    picker.delegate = self;
    picker.allowsEditing = YES;
    NSString *buttonTitleAtButtonIndex = [actionSheet buttonTitleAtIndex:buttonIndex];
    if ([buttonTitleAtButtonIndex isEqualToString:@"Take Photo"]) {
        // take photo...
        NSLog(@"User decided to take a photo");
        picker.sourceType = UIImagePickerControllerSourceTypeCamera;
        [self presentModalViewController:picker animated:YES];
    } else if ([buttonTitleAtButtonIndex isEqualToString:@"Choose Existing Photo"]) {
        // choose existing photo...
        NSLog(@"User decided to pick from the gallery");
        picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
        [self presentModalViewController:picker animated:YES];
    }

}

4. We’ve also kindly informed the compiler that we will be doing stuff with the actual image picker.  So let’s do that too.  First, let’s create a method for when the Cancel button is touched.

- (void)imagePickerControllerDidCancel:(UIImagePickerController *) Picker {

    NSLog(@"imagePickerControllerDidCancel called");
    [Picker dismissModalViewControllerAnimated:YES];

}

Now all you have to do is implement the method to actually do something with the photo, taken with the camera or picked from the gallery. That’s up to you.

- (void)imagePickerController:(UIImagePickerController *) Picker didFinishPickingMediaWithInfo:(NSDictionary *)info {

}

That’s all folks! :>

But if it helps, let me give you my implementation method so you can use some of the code if you wish. You’ll see a couple of things, I put in some comments and extra code (that compiles fine) for you that aren’t in my code per se, but just to help you walk along.

- (void)imagePickerController:(UIImagePickerController *) Picker didFinishPickingMediaWithInfo:(NSDictionary *)info {

    NSLog(@"didFinishPickingMediaWithInfo called");
    UIImage *selectedImage = [info objectForKey:UIImagePickerControllerEditedImage];

    //allows editing is TRUE, if you will notice in my picker alloc.
    //scale box down to 200x200 - reference above kangaroo cropper
    float actualHeight = 200.0;
    float actualWidth = 200.0;
    CGRect rect = CGRectMake(0.0, 0.0, actualWidth, actualHeight);
    UIGraphicsBeginImageContext(rect.size);
    [selectedImage drawInRect:rect];
    UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    //dismiss the picker
    [Picker dismissModalViewControllerAnimated:YES];

    //get a path to the local documents directory
    NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    NSString *pathToProfilePicture = [NSString pathWithComponents:[NSArray arrayWithObjects:documentsDirectory, @"userprofilepicture", nil]];

    //delete the local profile picture, if one exists already
    BOOL profilePictureFileExists = [[NSFileManager defaultManager] fileExistsAtPath:pathToProfilePicture];
    if (profilePictureFileExists) {
        NSLog(@"User profile picture exists, we will delete it now");
        [[NSFileManager defaultManager] removeItemAtPath:pathToProfilePicture error:nil];
    }

    //now create the file
    BOOL createdFileOk = [[NSFileManager defaultManager] createFileAtPath:pathToProfilePicture contents:nil attributes:nil];
    if (!createdFileOk) {
        NSLog(@"Error creating profile picture image file to write to on disk %@", pathToProfilePicture);
    } else {
        NSLog(@"Writing profile picture image file to disk...");
        NSFileHandle* profilePictureHandle = [NSFileHandle fileHandleForWritingAtPath:pathToProfilePicture];
        [profilePictureHandle writeData:UIImagePNGRepresentation(croppedImage)];
        [profilePictureHandle closeFile];
        _pictureProfileDirtyBit = @"N";
        NSLog(@"Writing profile picture complete");

        //show the image you just saved
        [_profilePictureButton setImage:croppedImage forState:UIControlStateNormal];

        //now asynchronously bump this picture up to the web
        //I might give you the guts of how to upload something like this to a web service listening for such information, but not in this post
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
        dispatch_async(queue, ^{[self uploadMyNewPictureToWeb];});

    }

}

I’d love to know if there’s a better way to accomplish this or if you used a similar approach for something of your own.

Advertisements
Leave a comment

1 Comment

  1. Teleporting a Kangaroo « about ss

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: