Tis the season to be goofy! And since it’s that merry time of the year I thought it would be fun to create an app where you could add Santa’s beard to any photo and share it. I call it “Bearded”.

What You Need

Besides a Mac and an iPhone, you will need basic iOS programming skills. Don’t have them? Fear not, we provide an excellent iPhone programming course taught by none other than yours truly. Head on over to teamtreehouse.com.

Preview

To get you all excited about this tutorial here’s a preview of what the app can do.

[youtube http://www.youtube.com/watch?v=e7JD3BC_qmI&w=640&h=360]

Interface Design

Hope you have your coffee or tea because you need to be awake for this one! Ok, let’s dive right in. Open up Xcode and create a new Single View application template. Make sure you select the options for Use Storyboard and Use Automatic Reference Counting.

Once you have created your project it should look something like the screenshot below with a single app delegate and a view controller.

Project Summary

At the core of this application are two image views (UIImageView), one for the photo and the other for Santa’s beard. While the photo image view will remain in a fixed position, Santas beard will have gestures to enable manipulation of size and position.

Open up the storyboard and add two image views.
Image Views

Create IBOutlets for both the image views.
Creating IBOutlets

Before going any further we need to turn off Autolayouts. “But why?”, you ask. “It’s the best thing in Xcode 4.5”. Well, true but it has a bad habit of automatically creating constraints when they’re not called for. Besides, our app is way too simple to need any constraining. So how do you turn off Autolayout? Simple, click anywhere on the canvas and then select the File inspector in the Utility area. Somewhere down the middle you will notice a checkbox saying ‘Use Autolayout’, un-check it.

Disable Autolayout

While we have the storyboard open let’s go ahead and finish up the interface. We need to add a toolbar with two buttons. One to capture the photo and the other for sharing the finished image. To accomodate the toolbar we will need to reduce the height of the Photo image view by 44px (548 – 44 = 504) because the height of the toolbar is 44px.

Add a toolbar towards the bottom. After the first button, add a Flexible Space bar and then another bar button item. Your interface should now look something like this:

Adding a toolbar

Want to make your design more festive? Why not add a tint color to your toolbar? The identifiers for the two buttons are Camera and Action respectively.

Finally, I’ve created two images:

  1. A default image for the Photo view (download image)
  2. The beard image (download image)

Add both images to your project and select them as the default image for each of the image views.

Adding default images

Adding Gestures

We want the user to be able to move the beard around and size it to fit their photo and that’s where the gesture recognizers come in handy.

UIGestureRecognizer is an abstract base class that has been part of the iOS SDK since version 3.2. It’s an abstract class because it has concrete subclasses for recognizing specific gestures. We will be using two gesture recognizers:

  • UIPinchGestureRecognizer: The pinch gesture is achieved using two fingers. When the fingers are brought closer to each other it is percieved as zooming-out and the reverse is zooming-in. In our case, we will make the beard image larger or smaller based on the distance between the two fingers.
  • UIPanGestureRecognizer: The panning or dragging gesture is achieved using one or more fingers. We will use this gesture to move the beard around for placement.

We will add both the gesture recognizers to the viewDidLoad method of our THViewController.m.

- (void)viewDidLoad
{
    [super viewDidLoad];

    UIPinchGestureRecognizer *pinchGestureRecognizer =
    [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchZoomBeard:)];
    pinchGestureRecognizer.delegate = self;
    [self.beardView addGestureRecognizer:pinchGestureRecognizer];

    UIPanGestureRecognizer  *panGestureRecognizer =
    [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panBeard:)];
    panGestureRecognizer.maximumNumberOfTouches = 1;
    panGestureRecognizer.delegate = self;
    [self.beardView addGestureRecognizer:panGestureRecognizer];
}

The gesture recognizers use the Target-Action design pattern. The reason we are setting the panGestureRecognizer.maximumNumberOfTouches to 1 so that the two gestures don’t interfere with each other. We want to make sure that when there are two fingers the pinch gesture is triggered and when there is only one finger present then the pan gesture is triggered.

Next we implement the two selectors pinchZoomBeard and panBeard.

- (void) pinchZoomBeardView:(UIPinchGestureRecognizer *)sender {

    UIView *beard = [sender view];

    // The scale factor between the two fingers
    CGFloat factor = [sender scale];

    // Apply transformation only for the beginning or changing states
    if (sender.state == UIGestureRecognizerStateBegan || sender.state == UIGestureRecognizerStateChanged )  {

        // Apply an affine transformation based on the factor
        beard.transform = CGAffineTransformMakeScale(factor, factor);

    }    
}

The CGAffineTransformMakeScale function returns a CGAffineTransform struct to scale a coordinate system which is used by Quartz to scale the coordinates of a point (x,y).

Panning or dragging is a bit more involved because you have to capture the movement of the finger over time relative to the coordinate system of a specific view.

- (void) panBeardView:(UIPanGestureRecognizer *)sender {
    UIView *beard = [sender view];      

    if ([sender state] == UIGestureRecognizerStateBegan || [sender state] == UIGestureRecognizerStateChanged) {

        // Get the panning move point relative to the parent view
        CGPoint translation = [sender translationInView:[beard superview]];
        // NSLog(@"x: %1.2f, y: %1.2f",translation.x,translation.y);

        // Add it to the center point of the beard view so that it stays
        // under the finger of the user
        [beard setCenter:CGPointMake([beard center].x + translation.x, [beard center].y + translation.y)];

         // Reset the translation to reduce panning velocity
         // Removing this line will result in the beard view disappearing very quickly
        [sender setTranslation:CGPointZero inView:[beard superview]];

    }               
}

The comments in the code above explain what’s going on in each line. However, it is important to note that the method translationInView returns x,y coordinates based on time. The resulting coordinates are the distance from the origin when the panning began. However, we reset those coordinates in the third line with the setTranslation:CGPointZero method. Try uncommenting the NSLog to check the resulting values. In addition, try commenting out the setTranslation:CGPointZero line and see how it affects the panning velocity. It will help you understand the code better.

Important Note

If the beard view is not responding to gestures then it means that you do not have the user interaction property enabled. Add the following line of code to your viewDidLoad:

    self.beardView.userInteractionEnabled = YES;

To be continued..

This concludes Part 1 of a two part series. By now you should have the basic view set up and the ability to move and size your beard around the screen. If you’ve achieved this much then well done!

Still more to come! In Part 2, we will learn the following:

  • Using camera controls to take a photo or pick from the camera roll.
  • Blending or merging the photo and beard image views to create a single composite image.
  • Using the Social framework to share our beards on Facebook, Twitter and Email.
  • Complete source code.
  • Link to download and try out the app from the app store.