Sunday, April 16, 2017

Scan QR Code / Bar Code Using AVFoundation Framework (Objective c)

10:54 PM Posted by CHANDAN MAKHIJA No comments
AV Foundation is the full featured framework for working with time-based audiovisual media on iOS, macOS, watchOS and tvOS. Using AV Foundation, you can easily play, create, and edit QuickTime movies and MPEG-4 files, play HLS streams, and build powerful media functionality into your apps.

AVFoundation incorporates,  the ability to discover and read bar codes in real-time. Any barcode scanning, including QR codes, is totally based on video capturing, that’s why AVFoundation framework supports the barcode reading feature. Keep this info on mind, as it’s the starting point for the entire application.

First of all add UIView view, which is going to be the container for the preview layer of the video captured by the device.

Here is how the demo application is going to work:

Add the camera button on the UIViewController. When you will click on this button a UIView will be loaded that will contain the preview of recording. When you will start recording any Barcode , it will capture it and display the information captured on UITextField.

Also, a sound effect (a beep sound) is being played every time a QR code has been successfully read to make our app more vivid and interactive.

Note that this application cannot be tested on the Simulator, neither to a device without a camera, as everything is based on real-time video capturing. Therefore, you’ll need to connect your device and run it there if you want to see the application live.

First of all make XIB that contains a UIView with Black BackGround, as shown below





 Make a class named "QRScanView"

Add following code in "QRScanView.h"


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

@interface QRScanView : UIView <AVCaptureMetadataOutputObjectsDelegate>

// connect your Xib here
@property (weak, nonatomic) IBOutlet UIView *scanView;

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

@property  BOOL isReading;

@property void(^barCodeScanned)(NSString*);

+ (instancetype) loadScanner;

- (void)showScanner:(UIViewController*) vc;
- (void) getScannedValue:(void(^)(NSString* message))callback;


@end


Add following code in "QRScanView.m"


#import "QRScanView.h"

static QRScanView* sSharedInstance;
static QRScanView* _scanner ;

@interface QRScanView()
{
    NSString* _serviceTag;
}

@end

@implementation QRScanView

+ (instancetype) loadScanner
{
    if(!sSharedInstance)
    {
        // load your xib on UIView here
        sSharedInstance = [[[NSBundle mainBundle] loadNibNamed:@"QRScanView" owner:sSharedInstance options:nil] lastObject];
    }
    
    return  sSharedInstance;
}

- (void)showScanner:(UIViewController*) vc
{
    sSharedInstance.isReading = NO;
    sSharedInstance.captureSession = nil;
    sSharedInstance.center = CGPointMake(vc.view.frame.size.width  / 2,
                                  vc.view.frame.size.height / 2);
    sSharedInstance.layer.borderWidth = 1.0f;
    sSharedInstance.layer.borderColor = [UIColor lightGrayColor].CGColor;
    
    [sSharedInstance startReading];
    
    [vc.view addSubview:sSharedInstance];
}

- (void) startReading
{
    NSError *error;
    
    AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    
    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];
    if (!input)
    {
        NSLog(@"%@", [error localizedDescription]);
    
    }
    
    _captureSession = [[AVCaptureSession alloc] init];
    [_captureSession addInput:input];
    
    AVCaptureMetadataOutput *captureMetadataOutput = [[AVCaptureMetadataOutput alloc] init];
    [_captureSession addOutput:captureMetadataOutput];
    
    dispatch_queue_t dispatchQueue;
    dispatchQueue = dispatch_queue_create("myQueue", NULL);
    
    [captureMetadataOutput setMetadataObjectsDelegate:self queue:dispatchQueue];
    
     // include all types of barcode here
    [captureMetadataOutput setMetadataObjectTypes:[NSArray                 arrayWithObjects:AVMetadataObjectTypeQRCode, AVMetadataObjectTypeFace, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeUPCECode,AVMetadataObjectTypeAztecCode, AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeITF14Code,AVMetadataObjectTypeCode39Code, AVMetadataObjectTypeCode93Code, AVMetadataObjectTypePDF417Code, AVMetadataObjectTypeCode128Code, AVMetadataObjectTypeDataMatrixCode,AVMetadataObjectTypeCode39Mod43Code,AVMetadataObjectTypeInterleaved2of5Code, nil]];
    
    _videoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession];
    [_videoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
    [_videoPreviewLayer setFrame:self.scanView.layer.bounds];
    [self.scanView.layer addSublayer:_videoPreviewLayer];
    
    [_captureSession startRunning];
    
}

- (void) stopReading
{
    [_captureSession stopRunning];
    _captureSession = nil;
    
    [_videoPreviewLayer removeFromSuperlayer];
    
    // remove Xib from view here
    [self removeFromSuperview];
    
    self.barCodeScanned(_serviceTag);
}

// get scanned value here
- (void) getScannedValue:(void(^)(NSString* message))callback
{
    self.barCodeScanned = callback;
}

// Delegate methods for AVCaptureMetadataOutputObjects
- (void) captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
    if (metadataObjects != nil && [metadataObjects count] > 0)
    {
        AVMetadataMachineReadableCodeObject *metadataObj = [metadataObjects objectAtIndex:0];
        
        _serviceTag = [metadataObj stringValue];
        
        [self performSelectorOnMainThread:@selector(stopReading) withObject:nil waitUntilDone:NO];
       
        self.isReading = NO;
    }
}


@end



Now import "BarcodeScan.h" file in you ViewController

On IBAction of  button click, call the method of "QRScanView" class like this


- (IBAction)scanCodeBtnPressed:(id)sender
{
    QRScanView* scanner = [QRScanView loadScanner];

    self.scanCodeBtn.selected = !self.scanCodeBtn.isSelected;
    
    if(self.scanCodeBtn.selected)
        [scanner showScanner:self];
    
    [scanner getScannedValue:^(NSString *message) {
        //display scanner message on UITextField
        UITextField* textField = (UITextField*)[self.view viewWithTag:EServiceTag];
        textField.text = message;
    }];

}

0 comments:

Post a Comment