Sunday 9 November 2014

How to Scan Barcode using iOS native framework

Prior to iOS 7, It was a big problem for iOS developer to add a native QR code scanner. So some third party code library were used to achieve barcode scanning. But iOS 7 brings a bunch of new features. The AVFoundation framework is  one of them where some new API/classes are added, as a result AVFoundation provided the ability to discover and read bar codes in real time.

In this tutorial we are going to scan a QR Code, its known as Quick Response Code. QR Code is 2D barcode. Its design can have horizontal and vertical lines. So you can scan 1D (vertical lines) and 2D (Vertical and Horizontal lines).

What can be store in QR code ?
QR Code can contain information of like web URL, Phone number, SMS text and other Simple text.


Lets start how to Implement QR Code Scanning with Native iOS framework.

1- Open Xcode and create a new project  (I pick single view application  ), name it "BarcodeScanner".

2-Open Storyboard and Create a UIbutton to Start/Stop scanning, a UILabel to print scanning status/scanned information, And a UIView inside that a camera will be open to scan QR code.


QR Code scan and read using native iOS framework

3- Open ViewController.h and  define some properties

#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>

@interface ViewController : UIViewController<AVCaptureMetadataOutputObjectsDelegate>

@property (nonatomic, strong) AVCaptureSession *captureSession;
@property (nonatomic, strong) AVCaptureVideoPreviewLayer *videoPreviewLayer;
@property(nonatomic,assign) BOOL IsScanning;

@property(weak, nonatomic)IBOutlet UILabel *lblScanStatus_info;
@property(weak, nonatomic)IBOutlet UIButton *btnStartStop;
@property(weak, nonatomic)IBOutlet UIView *vwCameraPreview;

-(IBAction)StartStop_Scan:(id)sender;
@end



4- Connect IBOutlet from starboard, Connect "StartStop_Scan" method to Button.
Open ViewController.m
Define two private method one for Start Scan and Second one for Stop scan

@interface ViewController ()
-(void)StartScanning;
-(void)StopScanning;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    _IsScanning = NO; //
    
}

#pragma mark IBAction

-(IBAction)StartStop_Scan:(id)sender
{
    NSLog(@"Inside startstopmethod");
    //If start button  pressed
    if(!_IsScanning)
    {
        [self StartScanning];
        
    }
    ////If Stop button  pressed
    else
    {
        [self StopScanning];
        
    }
    
    //set the flag opposit to earlier ;
    _IsScanning = !_IsScanning;
}

Don't forget to check camera support in device, Simulator don't support camera so we need a device to run this tutorial.


-(void)StartScanning
{
    AVCaptureDevice *captureDevice = nil;
    AVCaptureDeviceInput *input = nil;
    //Check for device camera existance
    NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
    if ( [videoDevices count] > 0 ) // This device has one or more cameras
    {
        NSError *error;
        captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
        
        // Get an instance of the AVCaptureDeviceInput class
        input = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];
        
        if (!input) {
            // If any error occurs, simply log the description
            NSLog(@"error desc:%@", [error localizedDescription]);
            
        }
        else
        {
            // init the captureSession .
            _captureSession = [[AVCaptureSession alloc] init];
            // Set  input device at the capture session.
            [_captureSession addInput:input];
            
            
            // Initialization of a AVCaptureMetadataOutput object and set it to output sessioncapture
            AVCaptureMetadataOutput *captureMetadataOutput = [[AVCaptureMetadataOutput alloc] init];
            [_captureSession addOutput:captureMetadataOutput];
            
            // make a serial dispatch queue.
            dispatch_queue_t dispatchQueue;
            dispatchQueue = dispatch_queue_create("QRCodeScanQueue", NULL);
            [captureMetadataOutput setMetadataObjectsDelegate:self queue:dispatchQueue];
            [captureMetadataOutput setMetadataObjectTypes:[NSArray arrayWithObject:AVMetadataObjectTypeQRCode]];
            
            //  video preview layer and adding at subview _vwCameraPreview
            _videoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession];
            [_videoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
            [_videoPreviewLayer setFrame:_vwCameraPreview.layer.bounds];
            [_vwCameraPreview.layer addSublayer:_videoPreviewLayer];
            
            // Start video capturing.
            [_captureSession startRunning];
            
            [_btnStartStop setTitle:@"Stop Scan" forState:UIControlStateNormal];
            [_lblScanStatus_info setText:@"Scanning for QR Code:-----"];

        }
    }
    else
    {
        NSLog(@"Camera is not supported here");
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Alert" message:@"Current device does not supporting camera " delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
        
        [alert show];
        
    }
    
}

-(void)StopScanning
{
    // Stop video capture
    [_captureSession stopRunning];
    _captureSession = nil;
    
    // Remove video preview layer.
    [_videoPreviewLayer removeFromSuperlayer];
    
    [_btnStartStop setTitle:@"Start Scan" forState:UIControlStateNormal];
    [_lblScanStatus_info setText:@"QR Code Scan is not started"];
}

#pragma mark Implememntaion of AVCaptureMetadataOutputObjectsDelegate method

-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
    
    if (metadataObjects != nil && [metadataObjects count] > 0) {
        // Get the metadata object.
        AVMetadataMachineReadableCodeObject *metadataObj = [metadataObjects objectAtIndex:0];
        //Check QR type meta data object
        if ([[metadataObj type] isEqualToString:AVMetadataObjectTypeQRCode]) {
            // If the found metadata is equal to the QR code metadata then update the label status info text,
            
            // Proecees of main thread done.
            [_lblScanStatus_info performSelectorOnMainThread:@selector(setText:) withObject:[metadataObj stringValue] waitUntilDone:NO];
            //and stop reading
            [self performSelectorOnMainThread:@selector(StopScanning) withObject:nil waitUntilDone:NO];
            
            [_btnStartStop performSelectorOnMainThread:@selector(setTitle:) withObject:@"Start Scan" waitUntilDone:NO];
            
            _IsScanning = NO;
            
        }
    }
}

To test QR code scan you may find some images from Google and or use the image shown above for sample QR code.

Hope you will enjoyed this code. If you have any concern please share your comments.

No comments:

Post a Comment