In Part 1 we created the interface and added gesture recognizers for the beard image view. We still have three main features to complete: capturing a photo, blending or merging images and sharing the final image. So let’s get started.
Camera Controls
We want to provide the user with two options either to take a photo or pick one from the camera roll. For this we will display a UIActionSheet
to present the user with choices. Finally, we will use the simple UIImagePickerController
to facilitate camera or library functions.
Firstly, we will connect the camera button within the toolbar to an IBAction
called cameraBtnAction
.
The implementation of the cameraBtnAction
involves creating the action sheet:
- (void) cameraBtnAction:(UIButton *)sender {
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:nil
delegate:self
cancelButtonTitle:@"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:@"Take a Picture",@"Choose from Library", nil];
[actionSheet showInView:self.view];
}
Do not forget to add UIActionSheetDelegate
in the interface declaration of ViewController.h
.
@interface THViewController : UIViewController <UIGestureRecognizerDelegate,UIActionSheetDelegate>
We can intercept the user selection of the action sheet with the following delegate method.
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
// Create a new image picker instance:
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
// Set the image picker source:
switch (buttonIndex) {
case 0:
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
break;
case 1:
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
break;
default:
break;
}
picker.delegate = self;
// Show picker if Take a picture or Choose from Library is selected
if ( buttonIndex == 0 || buttonIndex == 1)
[self presentViewController:picker animated:YES completion:nil];
}
The method above receives the buttonIndex
as a parameter which contains the index
of the selected button. We create an instance of UIImagePickerController
and then based on the selection we set the sourceType
, which determines whether the user will take a picture or be allowed to pick one from the camera roll. Finally, the picker
objects delegate is set to self
which means we need to add two more delegates to our interface defintion.
@interface THViewController : UIViewController <UIImagePickerControllerDelegate, UINavigationControllerDelegate,UIGestureRecognizerDelegate,UIActionSheetDelegate>
After the photo is taken or picked, we need to set that image to our photoView
using the delegate method from the UIImagePickerControllerDelegate
.
- (void) imagePickerController: (UIImagePickerController *) picker
didFinishPickingMediaWithInfo: (NSDictionary *) info {
self.photoView.image = [info valueForKey:UIImagePickerControllerOriginalImage];
[self dismissViewControllerAnimated:YES completion:nil];
}
Blending or Merging Images
Now that you know how to implement the camera functionality and set the photoView
to an image taken or selected by the user, it is time to figure out how to merge the photo and beard into a single image.
UIKit contains some useful functions for image manipulation and one those is UIGraphicsBeginImageContext
which helps create a bitmap-based graphics context for a specified rectangular area. You can then draw anything to this context and capture the resulting image.
Here is what the code for the blendImage
method looks like:
- (UIImage *) blendImages {
UIImage *photoImage = self.photoView.image ;
UIImage *beardImage = self.beardView.image;
// Get the size of the photo
CGSize photoSize = CGSizeMake(photoImage.size.width, photoImage.size.height);
// Create a bitmap graphics context of the photoSize
UIGraphicsBeginImageContext( photoSize );
// Draw the photo in the specified rectangle area
[photoImage drawInRect:CGRectMake(0,0,photoSize.width,photoSize.height)];
CGPoint origin = self.beardView.frame.origin;
CGSize size = self.beardView.frame.size;
CGFloat xScale = photoImage.size.width / self.view.bounds.size.width;
CGFloat yScale = photoImage.size.height / (self.view.bounds.size.height-44);
// Draw the beard in the specified rectangle area
[beardImage drawInRect:CGRectMake((origin.x * xScale), (origin.y * yScale),
size.width * xScale, size.height * yScale)
blendMode:kCGBlendModeNormal alpha:1.0];
// Save the generated image to an image object
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
You are probably wondering about the two variables xScale
and yScale
. The photoView
is set to “Aspect Fit”, which means that the original photo is proportionately fitted to be displayed on the screen. The size of photo is much larger than what is displayed on the screen, therefore if we simply applied the screen size and position of the beard to be drawn then the resulting image would be of a tiny misplaced beard. After applying scaling the beard is increased in size and positioned relative to its screen position.
Social Framework
A new feature in iOS 6 is social framework which allows you to share content using various methods from within your app. All you have to do is specify which social functions you want to make available to the user and an action sheet is displayed to the user. Of course you have to provide it with the content that needs to be shared.
Just as we had connected the camera button to an IBAction
called cameraBtnAction
, similarly we are going to connect the share button to an IBAction
called shareBtnAction
.
Implementation of the shareBtnAction
.
- (void) shareBtnAction:(UIButton *)sender {
// An array of content that needs to posted
NSArray *activityItems = @[@"You have been Bearded!", UIImageJPEGRepresentation([self blendImages],0.75)];
// Create a new UIActivityViewController with the activityItems array
UIActivityViewController *activityController = [[UIActivityViewController alloc]
initWithActivityItems:activityItems applicationActivities:nil];
// Exclude activities that are irrelevant
activityController.excludedActivityTypes = @[UIActivityTypePostToWeibo,UIActivityTypePrint,UIActivityTypeCopyToPasteboard,UIActivityTypeAssignToContact];
// Present the activityController
[self presentViewController:activityController
animated:YES completion:nil];
}
iPhone 5 Compatibility
So far we have been creating the app without worrying about whether it will scale for the 4″ screen on the iPhone 5. You must be wondering since we disabled autolayout if it is at all possible. It is simpler than you think because we have a very basic application. All you need to do is add this line to your viewDidLoad
method.
self.photoView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
Now the photoImageView
will scale vertically for the iPhone 5. The taller screen also requires a taller default image. For that we need to check if the device is an iPhone 5 and for using a macro:
#define IS_IPHONE_5 ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )568 ) < DBL_EPSILON )
The macro uses the screen sizes to determine the height. Once again within the viewDidLoad
method we set the image “photo-tall.png” to the photoView
if ( IS_IPHONE_5 )
self.photoView.image = [UIImage imageNamed:@"photo-tall.png"];
Conclusion
There you have it, a fun and easy app that teaches you a whole lot and requires little time and effort to build. Happy Holidays and Get Bearded!
You can download the complete source code from my github:
https://github.com/abijlani/bearded
Coming soon you will be able to download the app directly from the app store.
Can I just say what a relief to uncover somebody that genuinely knows what they are talking about on the internet.
You definitely understand how to bring an issue to light and make it important.
More and more people really need to read this and understand this side
of the story. I can’t believe you aren’t more popular given that you certainly have
the gift.
Nice app. Really amusing!