0

I made an QR Code reading application with AVFoundation by tutorial on this site (tutorial of Appcoda). After reading QR code, the app shows an UIAlertView. But it takes nearly 2 minutes (sometimes more than 3mins). I'll paste the whole ViewController.m file here. I hope it is enough. (UIAlertView is in captureOutput method)

//
//  ViewController.m
//  Yuvio
//
//  Created by İhsan Batuğhan YILMAZ on 29/08/15.
//  Copyright © 2015 Farabius. All rights reserved.
//

#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>


@interface ViewController ()

@property (nonatomic) BOOL isReading;

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

-(BOOL)startReading;
-(void) stopReading;

@end

@implementation ViewController

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

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

-(IBAction)startStopReading:(id)sender {
    if (!_isReading) {
        if ([self startReading]) {
            [_btnStart setTitle:@"Stop"];
        }
    }
    else{
        [self stopReading];
        [_btnStart setTitle:@"Start!"];
    }

    _isReading = !_isReading;
}
- (BOOL)startReading {
    NSError *error;

    AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];
    if (!input) {
        NSLog(@"%@", [error localizedDescription]);
        return NO;
    }

    _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];
    [captureMetadataOutput setMetadataObjectTypes:[NSArray arrayWithObject:AVMetadataObjectTypeQRCode]];

    _videoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession];
    [_videoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
    [_videoPreviewLayer setFrame:_cameraView.layer.bounds];
    [_cameraView.layer addSublayer:_videoPreviewLayer];

    [_captureSession startRunning];

    return YES;
}

-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
    if (metadataObjects != nil && [metadataObjects count] > 0) {
        AVMetadataMachineReadableCodeObject *metadataObj = [metadataObjects objectAtIndex:0];
        if ([[metadataObj type] isEqualToString:AVMetadataObjectTypeQRCode]) {

            UIAlertView *alert = [[UIAlertView alloc]
                                  initWithTitle:@"QR Detected"
                                  message:[metadataObj stringValue]
                                  delegate:self
                                  cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
            [alert show];

            [self performSelectorOnMainThread:@selector(stopReading) withObject:nil waitUntilDone:NO];
            [_btnStart performSelectorOnMainThread:@selector(setTitle:) withObject:@"Start!" waitUntilDone:NO];
            _isReading = NO;
        }
    }
}
-(void)stopReading{
    [_captureSession stopRunning];
    _captureSession = nil;

    [_videoPreviewLayer removeFromSuperlayer];
    return;
}

@end

1 Answer 1

4

I think that problem is in using UI functions outside main thread. Try this code:

-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
    if (metadataObjects != nil && [metadataObjects count] > 0) {
        AVMetadataMachineReadableCodeObject *metadataObj = [metadataObjects objectAtIndex:0];
        if ([[metadataObj type] isEqualToString:AVMetadataObjectTypeQRCode]) {
            __weak ViewController *weakSelf = self;
            dispatch_async(dispatch_get_main_queue(), ^{
                [weakSelf processQRCode:metadataObj];
            });
        }
    }
}

-(void)processQRCode:(AVMetadataMachineReadableCodeObject *)codeObject{
    UIAlertView *alert = [[UIAlertView alloc]
                          initWithTitle:@"QR Detected"
                          message:[codeObject stringValue]
                          delegate:self
                          cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
    [alert show];

    [self stopReading];
    [_btnStart setTitle:@"Start!" forState:UIControlStateNormal];
    _isReading = NO;
}

I checked your view controller with this fix and it works fast.

3
  • If you think that problem is fixed you could mark answer as correct one. Commented Aug 31, 2015 at 7:39
  • Your answer worked me in single view application. Thanks again. But how can i use it in Tabbed Application. Because it seems like _weak ViewController doesn't work for Tabbed View Application.
    – cameloper
    Commented Sep 14, 2015 at 13:54
  • Ok i found it, simply delete _weak ViewController *weakSelf = self; and change use self instead of weakSelf while calling method.
    – cameloper
    Commented Sep 14, 2015 at 14:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.