iOS smooth signature capture example using quadratic bezier curve

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.

iOS smooth signature capture example iOS signature capture with name iOS signature using quadratic bezier curve

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



24 comments :

  1. As they made their cheap fifa 14 coins bed on the hard floor, the older angel saw a hole fifa 14 coins in the wall and repaired it. When the younger angel asked why, the older angel cheap fifa 14 coins replied, "Things aren't always what they seem."

    ReplyDelete
  2. Thanks for this post! I was stuck trying to find a great signature capture for ios and this one works great!

    One quick question, I am trying to save the png that is created to core data, what would I need to change in order to do that? should I just create a new managed object after the create png from uiimage code? thanks in advance

    ReplyDelete
  3. WOW! Finally something that actually works after struggling with the crap from Github. Now I have to integrate it into my storyboard based project

    ReplyDelete
    Replies
    1. Hi John,

      How did you get this to work as an actual View?
      I have a Swift project and through bridging added the Objective-C files.
      Everything executes correctly, I just have no idea how to get the view to work by clicking on a button.

      Regards,
      Natasha

      Delete
    2. HI Jhohn,

      I have mobile app in ruby on rails platform.
      I got a requirement to capture the signature and save it.
      So please can you share the view files.

      Regards,
      Vinod

      Delete
  4. Great Tutorial!

    I was able to get it working with Swift and it works great until the last screen after entering the name for signature.
    When I click "OK" the screen which should display the signature and name is all black.

    Any suggestions?

    ReplyDelete
  5. Helped me allot ......Great post thanks ..................:)

    ReplyDelete
  6. If you are looking for a Signature View that comes with a default style and the necessary buttons, you can use https://github.com/payworks/MPBSignatureViewController

    ReplyDelete
  7. If you are looking for a Signature View that comes with a default style and the necessary buttons, you can use https://github.com/payworks/MPBSignatureViewController

    ReplyDelete
  8. Hello, You tutorial i aswome. i just want to ask is it possible to retrieve all images from directory and display it in list for and user could select one and then use it. Or can we save it to Photo Library ?

    Thanking you in advance.
    Regards.

    ReplyDelete
  9. Great code! I used it with an existing ViewController on my Storyboard to which I'd already added a UIImageView. I made several adjustments so that it created an appropriately sized image when rendering the signature due to using AutoConstraints as well. All in all, you put together a great Codebase from which to add signature support with.

    ReplyDelete
  10. Made a swift version of the main class, posted it here:
    https://macruthberg.wordpress.com/2016/02/18/swift-version-of-code-for-drawing-signature-on-screen/

    ReplyDelete
  11. I want to store signature in sql database as png image using swift. can you help me?

    ReplyDelete
    Replies
    1. I created signature and capture in ipad photos but I want to create a png image and directly store in database with customer name.

      Delete
  12. When i am doing signature it starts getting blur? Any solution?

    ReplyDelete
    Replies
    1. have u got any solution for the same??

      Delete
    2. I just replace UIGraphicsBeginImageContext(imageFrame.size) this line with UIGraphicsBeginImageContextWithOptions(_imageFrame.size,NO,0.0) inside this method - (void)touchesMoved:(NSSet )touches withEvent:(UIEvent )event and it works fine for me :)

      Delete
  13. We are really grateful for your blog post. You will find a lot of approaches after visiting your post. Great work.
    hotmail login | red ball

    ReplyDelete
  14. The blog or and best that is extremely useful to keep I can share the ideas of the future as this is really what I was looking for, I am very comfortable and pleased to come here. Thank you very much.
    animal jam | five nights at freddy's | hotmail login

    ReplyDelete
  15. I have implemented the same code in X-code 8.2 but unable to get a proper signature, signature gets blured have a look at this image [IMG]http://i66.tinypic.com/258p9oz.png[/IMG]
    please help me..

    ReplyDelete
  16. Thanks for your article! I have been looking for quite a long time and fortunately I read this article! I wish you would continue to have valuable articles like this or more to share with everyone!

    ReplyDelete
  17. This is one of the cult game now, a lot of people enjoy playing them . Also you can refer to the game :
    animal jam 2 | five nights at freddys 2 | hotmail login

    ReplyDelete