By Adam Nagy
In preparation for my AU presentation I created a few samples. One of those enables you to take a picture then place an existing picture on top of that and manipulate it (rotate, scale, move) - I'll talk about the others in other posts.
We were looking for partner applications on mobile devices to show to attendees at our Developer Days conferences and at AU. One of those is a simple yet powerful application called JobViewer by FabCAD: https://itunes.apple.com/az/app/jobviewer/id526728663?mt=8
It enables you to show what a given gate would look like at the exact future location.
Previously sales people would only provide brochures and so the buyers could only use a "poor man's augmented reality approach" :) - i.e. hold the brochure in front of them when looking at the place where the gate was planned to be placed. Now with this program people can take a picture of the place and then merge the picture of the gate on top of it. This makes it much easier to imagine/see how the gate will really fit in the environment. Not to mention that you can also easily take a screenshot on your mobile device and send this picture around to family or other people involved in deciding about the gate. This could work with many other products as well of course.
So let's see how you could write a program like that.
1) We can start with a Single View Application in Xcode
2) Then we can add an Image View, then a Toolbar and an extra Bar Button Item on our view in the storyboard
3) The Identifier of one of the button's can be set to Camera and the other one to Search
4) We need to hook those up with the code. The easiest way to do that is if we set the Editor to Show the Assistant Editor
, so that we can see two documents at the same time. Then we can open the storyboard in one view and open ViewController.h in the other one. Now we can simply Ctrl-drag the tool bar item into the code area. When the drag is finished a little dialog pops up where we can set if we want to use the control's event/action or create a reference to it (e.g. if we wanted to query or modify its properties later on)
This time we just need to handle the action, so let's go with that
5) We need to hook the other button up as well in a similar fashion. Let's call its action findPic, then create a reference to the Image View the same way but this time set Connection to Outlet and Name to imageView
6) On the net we can find sample codes for both taking a picture and accessing the pictures on the mobile device. Using those we can implement the startCamera and findPic functions in the ViewController.m file
7) In order to handle the gestures we can simply drag and drop Gesture Recognizers onto the Image View in the storyboard
8) Once the Rotation, Pan and Pinch Gesture Recognizers are added to the view we can hook them up to the code the same way as we did with the buttons, Ctrl-dragging them into the code area selecting Action as Outlet and naming the handler functions rotate, pan and pinch
9) There are multiple ways we can manipulate the image. This time we'll use setTransform for rotation and scaling (pinch), and setCenter for panning
Code of ViewController.h:
//
// ViewController.h
// PicOnPic
//
// Created by Adam Nagy on 20/10/2012.
// Copyright (c) 2012 Adam Nagy. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController<
UINavigationControllerDelegate,
UIImagePickerControllerDelegate,
UIPopoverControllerDelegate>
- (IBAction)startCamera:(id)sender;
- (IBAction)findPic:(id)sender;
@property (weak, nonatomic) IBOutlet UIImageView * imageView;
@property (strong, nonatomic) UIImageView * overlayImageView;
@property (strong, nonatomic) UIPopoverController * popoverController;
- (IBAction)rotate:(UIRotationGestureRecognizer *)sender;
- (IBAction)pinch:(UIPinchGestureRecognizer *)sender;
- (IBAction)pan:(UIPanGestureRecognizer *)sender;
@end
Code of ViewController.m:
//
// ViewController.m
// PicOnPic
//
// Created by Adam Nagy on 20/10/2012.
// Copyright (c) 2012 Adam Nagy. All rights reserved.
//
#import "ViewController.h"
#import <MobileCoreServices/MobileCoreServices.h>
@interface ViewController ()
@end
@implementation ViewController
@synthesize popoverController;
@synthesize overlayImageView;
- (void)viewDidLoad
{
NSLog(@"viewDidLoad");
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning
{
NSLog(@"didReceiveMemoryWarning");
[super didReceiveMemoryWarning];
}
- (IBAction)startCamera:(id)sender
{
NSLog(@"startCamera");
if (popoverController)
{
[popoverController dismissPopoverAnimated:YES];
popoverController = nil;
return;
}
if ([UIImagePickerController isSourceTypeAvailable:
UIImagePickerControllerSourceTypeCamera])
{
UIImagePickerController * imagePicker =
[[UIImagePickerController alloc] init];
imagePicker.delegate = self;
imagePicker.sourceType =
UIImagePickerControllerSourceTypeCamera;
imagePicker.mediaTypes = [NSArray arrayWithObjects:
(NSString *) kUTTypeImage,
nil];
imagePicker.allowsEditing = NO;
[self presentViewController:imagePicker
animated:YES completion:nil];
}
}
- (IBAction)findPic:(id)sender
{
NSLog(@"findPic");
if (popoverController)
{
[popoverController dismissPopoverAnimated:YES];
popoverController = nil;
return;
}
if ([UIImagePickerController isSourceTypeAvailable:
UIImagePickerControllerSourceTypeSavedPhotosAlbum])
{
UIImagePickerController * imagePicker =
[[UIImagePickerController alloc] init];
imagePicker.delegate = self;
imagePicker.sourceType =
UIImagePickerControllerSourceTypePhotoLibrary;
imagePicker.mediaTypes = [NSArray arrayWithObjects:
(NSString *) kUTTypeImage,
nil];
imagePicker.allowsEditing = NO;
popoverController = [[UIPopoverController alloc]
initWithContentViewController:imagePicker];
popoverController.delegate = self;
[popoverController presentPopoverFromBarButtonItem:sender
permittedArrowDirections:UIPopoverArrowDirectionUp
animated:YES];
}
}
- (void)imagePickerController:
(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSLog(@"imagePickerController");
[picker dismissViewControllerAnimated:YES completion:nil];
UIImage * image =
[info objectForKey:UIImagePickerControllerOriginalImage];
// If it's a picture selection
if (popoverController)
{
[popoverController dismissPopoverAnimated:YES];
popoverController = nil;
if (overlayImageView)
[overlayImageView removeFromSuperview];
overlayImageView = [[UIImageView alloc] initWithImage:image];
[self.imageView addSubview:overlayImageView];
}
// If it's taken by the camera
else
{
[self.imageView setImage:image];
}
}
- (BOOL)popoverControllerShouldDismissPopover:
(UIPopoverController *)popoverController
{
NSLog(@"popoverControllerShouldDismissPopover");
return YES;
}
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController
{
NSLog(@"popoverControllerDidDismissPopover");
self.popoverController = nil;
}
- (IBAction)pinch:(UIPinchGestureRecognizer *)sender
{
NSLog(@"pinch");
static CGAffineTransform origTr;
if (sender.state == UIGestureRecognizerStateBegan)
{
origTr = overlayImageView.transform;
}
else if (sender.state == UIGestureRecognizerStateChanged)
{
CGFloat scale = [sender scale];
CGAffineTransform tr =
CGAffineTransformConcat(
origTr,
CGAffineTransformMakeScale(scale, scale));
[self.overlayImageView setTransform:tr];
}
}
- (IBAction)pan:(UIPanGestureRecognizer *)sender
{
NSLog(@"pan");
static CGPoint prevPt;
if (sender.state == UIGestureRecognizerStateBegan)
{
prevPt = [sender locationInView:self.imageView];
}
else if (sender.state == UIGestureRecognizerStateChanged)
{
CGPoint pt = [sender locationInView:self.imageView];
CGAffineTransform tr =
CGAffineTransformMakeTranslation(
pt.x - prevPt.x,
pt.y - prevPt.y);
CGPoint cp =
CGPointApplyAffineTransform(self.overlayImageView.center, tr);
[self.overlayImageView setCenter:cp];
prevPt = pt;
}
}
- (IBAction)rotate:(UIRotationGestureRecognizer *)sender
{
NSLog(@"rotate");
static CGAffineTransform origTr;
if (sender.state == UIGestureRecognizerStateBegan)
{
origTr = overlayImageView.transform;
}
else if (sender.state == UIGestureRecognizerStateChanged)
{
CGFloat rotation = [sender rotation];
// Scale
CGAffineTransform tr =
CGAffineTransformConcat(
origTr,
CGAffineTransformMakeRotation(rotation));
[self.overlayImageView setTransform:tr];
}
}
@end
That is all really. The project is attached: Download PicOnPic_2012-12-04
You just need to place some images on your device, perferably with some transparent parts, and then off you go. :)
Note: I've only tested the code on iPad, since I have no iPhone.
Comments
You can follow this conversation by subscribing to the comment feed for this post.