Signatures are stylized scripts associating them to a given individual and primarily used for the purpose of displaying intent of that individual in regards to a given document by having his or her signature. Now with the advent of the mobile devices such as iPhone and iPad there is an everyday demand to capture individual's signature on electronic documents such as PDF and mobile applications. In this example we take advantage of the UITouch and UIGraphics class to capture the signature in an PNG image. To make our signature smooth we use the core graphics quadratic Bézier curve method instead of drawing straight lines between our touch points.
Interface file for the App Delegate - SignatureCaptureAppDelegate.h
#import <UIKit/UIKit.h>
#import "MyViewController.h"
@interface SignatureCaptureAppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) UINavigationController *navigationController;
@property (strong, nonatomic) MyViewController *myViewController;
@end
Implementation file for the App Delegate - SignatureCaptureAppDelegate.m
#import "SignatureCaptureAppDelegate.h"
@implementation SignatureCaptureAppDelegate
@synthesize navigationController;
@synthesize myViewController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
//create the navigation controller and add the controllers view to the window
navigationController = [[UINavigationController alloc] init];
[self.window addSubview:[self.navigationController view]];
//check if the viewcontroller exists, otherwise create it
if(self.myViewController == nil)
{
MyViewController *inputView = [[MyViewController alloc] init];
self.myViewController = inputView;
}
//push the viewcontroller into the navigation view controller stack
[self.navigationController pushViewController:self.myViewController animated:YES];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
@end
Interface file for the Signature capture View Controller - MyViewController.h
#import <UIKit/UIKit.h>
#import "DisplaySignatureViewController.h"
@interface MyViewController : UIViewController <UIAlertViewDelegate>
@property (nonatomic, strong) UIImageView *mySignatureImage;
@property (nonatomic, assign) CGPoint lastContactPoint1, lastContactPoint2, currentPoint;
@property (nonatomic, assign) CGRect imageFrame;
@property (nonatomic, assign) BOOL fingerMoved;
@property (nonatomic, assign) float navbarHeight;
@property (strong, nonatomic) DisplaySignatureViewController *displaySignatureViewController;
@end
Implementation file for the Signature capture View Controller - MyViewController.m
#import "MyViewController.h"
@interface MyViewController ()
@end
@implementation MyViewController
@synthesize mySignatureImage;
@synthesize lastContactPoint1, lastContactPoint2, currentPoint;
@synthesize imageFrame;
@synthesize fingerMoved;
@synthesize navbarHeight;
@synthesize displaySignatureViewController;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
//set the title of the navigation view
[self.navigationItem setTitle:@"Sign here"];
//create a save button in the navigation bar
UIBarButtonItem *myButton = [[UIBarButtonItem alloc]
initWithTitle:@"Save"
style:UIBarButtonItemStylePlain
target:self
action:@selector(saveSignature:)];
[self.navigationItem setRightBarButtonItem:myButton];
//set the view background to light gray
self.view.backgroundColor = [UIColor lightGrayColor];
//get reference to the navigation frame to calculate navigation bar height
CGRect navigationframe = [[self.navigationController navigationBar] frame];
navbarHeight = navigationframe.size.height;
//create a frame for our signature capture based on whats remaining
imageFrame = CGRectMake(self.view.frame.origin.x+10,
self.view.frame.origin.y-5,
self.view.frame.size.width-20,
self.view.frame.size.height-navbarHeight-30);
//allocate an image view and add to the main view
mySignatureImage = [[UIImageView alloc] initWithImage:nil];
mySignatureImage.frame = imageFrame;
mySignatureImage.backgroundColor = [UIColor whiteColor];
[self.view addSubview:mySignatureImage];
}
//when one or more fingers touch down in a view or window
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
//did our finger moved yet?
fingerMoved = NO;
UITouch *touch = [touches anyObject];
//just clear the image if the user tapped twice on the screen
if ([touch tapCount] == 2) {
mySignatureImage.image = nil;
return;
}
//we need 3 points of contact to make our signature smooth using quadratic bezier curve
currentPoint = [touch locationInView:mySignatureImage];
lastContactPoint1 = [touch previousLocationInView:mySignatureImage];
lastContactPoint2 = [touch previousLocationInView:mySignatureImage];
}
//when one or more fingers associated with an event move within a view or window
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
//well its obvious that our finger moved on the screen
fingerMoved = YES;
UITouch *touch = [touches anyObject];
//save previous contact locations
lastContactPoint2 = lastContactPoint1;
lastContactPoint1 = [touch previousLocationInView:mySignatureImage];
//save current location
currentPoint = [touch locationInView:mySignatureImage];
//find mid points to be used for quadratic bezier curve
CGPoint midPoint1 = [self midPoint:lastContactPoint1 withPoint:lastContactPoint2];
CGPoint midPoint2 = [self midPoint:currentPoint withPoint:lastContactPoint1];
//create a bitmap-based graphics context and makes it the current context
UIGraphicsBeginImageContext(imageFrame.size);
//draw the entire image in the specified rectangle frame
[mySignatureImage.image drawInRect:CGRectMake(0, 0, imageFrame.size.width, imageFrame.size.height)];
//set line cap, width, stroke color and begin path
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 3.0f);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 0.0, 0.0, 0.0, 1.0);
CGContextBeginPath(UIGraphicsGetCurrentContext());
//begin a new new subpath at this point
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), midPoint1.x, midPoint1.y);
//create quadratic Bézier curve from the current point using a control point and an end point
CGContextAddQuadCurveToPoint(UIGraphicsGetCurrentContext(),
lastContactPoint1.x, lastContactPoint1.y, midPoint2.x, midPoint2.y);
//set the miter limit for the joins of connected lines in a graphics context
CGContextSetMiterLimit(UIGraphicsGetCurrentContext(), 2.0);
//paint a line along the current path
CGContextStrokePath(UIGraphicsGetCurrentContext());
//set the image based on the contents of the current bitmap-based graphics context
mySignatureImage.image = UIGraphicsGetImageFromCurrentImageContext();
//remove the current bitmap-based graphics context from the top of the stack
UIGraphicsEndImageContext();
//lastContactPoint = currentPoint;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
//just clear the image if the user tapped twice on the screen
if ([touch tapCount] == 2) {
mySignatureImage.image = nil;
return;
}
//if the finger never moved draw a point
if(!fingerMoved) {
UIGraphicsBeginImageContext(imageFrame.size);
[mySignatureImage.image drawInRect:CGRectMake(0, 0, imageFrame.size.width, imageFrame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 3.0f);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 0.0, 0.0, 0.0, 1.0);
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
CGContextFlush(UIGraphicsGetCurrentContext());
mySignatureImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
}
//calculate midpoint between two points
- (CGPoint) midPoint:(CGPoint )p0 withPoint: (CGPoint) p1 {
return (CGPoint) {
(p0.x + p1.x) / 2.0,
(p0.y + p1.y) / 2.0
};
}
//save button was clicked, its time to save the signature
- (void) saveSignature:(id)sender {
//get reference to the button that requested the action
UIBarButtonItem *myButton = (UIBarButtonItem *)sender;
//check which button it is, if you have more than one button on the screen
//you must check before taking necessary action
if([myButton.title isEqualToString:@"Save"]){
NSLog(@"Clicked on the bar button");
//display an alert to capture the person's name
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Saving signature with name"
message:@"Please enter your name"
delegate:self
cancelButtonTitle:@"Cancel"
otherButtonTitles:@"Ok", nil];
[alertView setAlertViewStyle:UIAlertViewStylePlainTextInput];
[alertView show];
}
}
//some action was taken on the alert view
- (void) alertView:(UIAlertView *)alertView
clickedButtonAtIndex:(NSInteger)buttonIndex{
//which button was pressed in the alert view
NSString *buttonTitle = [alertView buttonTitleAtIndex:buttonIndex];
//user wants to save the signature now
if ([buttonTitle isEqualToString:@"Ok"]){
NSLog(@"Ok button was pressed.");
NSLog(@"Name of the person is: %@", [[alertView textFieldAtIndex:0] text]);
NSString * personName = [[alertView textFieldAtIndex:0] text];
//create path to where we want the image to be saved
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:@"MyFolder"];
//if the folder doesn't exists then just create one
NSError *error = nil;
if (![[NSFileManager defaultManager] fileExistsAtPath:filePath])
[[NSFileManager defaultManager] createDirectoryAtPath:filePath
withIntermediateDirectories:NO
attributes:nil
error:&error];
//convert image into .png format.
NSData *imageData = UIImagePNGRepresentation(mySignatureImage.image);
NSString *fileName = [filePath stringByAppendingPathComponent:
[NSString stringWithFormat:@"%@.png", personName]];
//creates an image file with the specified content and attributes at the given location
[fileManager createFileAtPath:fileName contents:imageData attributes:nil];
NSLog(@"image saved");
//check if the display signature view controller doesn't exists then create it
if(self.displaySignatureViewController == nil){
DisplaySignatureViewController *displayView = [[DisplaySignatureViewController alloc] init];
self.displaySignatureViewController = displayView;
}
//pass the person's name to the next view controller
self.displaySignatureViewController.personName = personName;
//tell the navigation controller to push a new view into the stack
[self.navigationController pushViewController:self.displaySignatureViewController animated:YES];
}
//just forget it
else if ([buttonTitle isEqualToString:@"Cancel"]){
NSLog(@"Cancel button was pressed.");
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
Interface file for the Signature display View Controller - DisplaySignatureViewController.h
#import <UIKit/UIKit.h>
@interface DisplaySignatureViewController : UIViewController
@property (nonatomic, strong) NSString *personName;
@property (nonatomic, strong) UILabel *signedBy;
@property (nonatomic, strong) UIImageView *mySignatureView;
@end
Implementation file for the Signature display View Controller - DisplaySignatureViewController.m
#import "DisplaySignatureViewController.h"
@interface DisplaySignatureViewController ()
@end
@implementation DisplaySignatureViewController
@synthesize personName;
@synthesize signedBy;
@synthesize mySignatureView;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
//set the title of the navigation view
[self.navigationItem setTitle:@"Display Signature"];
//create a label to display the name of the person who signed the document
CGRect myFrame = CGRectMake(10.0f, 0.0f, 300.0f, 30.0f);
signedBy = [[UILabel alloc] initWithFrame:myFrame];
signedBy.font = [UIFont boldSystemFontOfSize:16.0f];
signedBy.textAlignment = NSTextAlignmentLeft;
[self.view addSubview:signedBy];
//get reference to the navigation frame to calculate bar height
CGRect navigationframe = [[self.navigationController navigationBar] frame];
int navbarHeight = navigationframe.size.height;
//frame for our signature image
CGRect imageFrame = CGRectMake(self.view.frame.origin.x+10, 30,
self.view.frame.size.width-20,
self.view.frame.size.height-navbarHeight-30);
//create an image view to display our signature image
self.mySignatureView = [[UIImageView alloc] init];
[self.mySignatureView setFrame:imageFrame];
[self.mySignatureView setContentMode:UIViewContentModeScaleAspectFit];
[self.view addSubview:self.mySignatureView];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:YES];
//who signed the document
signedBy.text = [NSString stringWithFormat:@"Signed by: %@", personName];
//create the path to our image file
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:@"MyFolder"];
NSString *fileName = [filePath stringByAppendingPathComponent:
[NSString stringWithFormat:@"%@.png", personName]];
//get the contents of the image file into the image
UIImage *signature = [UIImage imageWithContentsOfFile:fileName];
//display our signature image
mySignatureView.image = signature;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
Related Posts
No comments:
Post a Comment
NO JUNK, Please try to keep this clean and related to the topic at hand.
Comments are for users to ask questions, collaborate or improve on existing.