iOS 7 introduced APIs to read bar codes using the camera. I could have easily overlooked it without this excellent post from doubleencore.
NSHipster provided sample code, but it missed some details to work. Here is a sample which does.
Sample code
@import AVFoundation; @interface CEViewController () <AVCaptureMetadataOutputObjectsDelegate> @property (strong) AVCaptureSession *captureSession; @end @implementation CEViewController - (void)viewDidLoad { [super viewDidLoad]; self.captureSession = [[AVCaptureSession alloc] init]; AVCaptureDevice *videoCaptureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; NSError *error = nil; AVCaptureDeviceInput *videoInput = [AVCaptureDeviceInput deviceInputWithDevice:videoCaptureDevice error:&error]; if(videoInput) [self.captureSession addInput:videoInput]; else NSLog(@"Error: %@", error); AVCaptureMetadataOutput *metadataOutput = [[AVCaptureMetadataOutput alloc] init]; [self.captureSession addOutput:metadataOutput]; [metadataOutput setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()]; [metadataOutput setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN13Code]]; AVCaptureVideoPreviewLayer *previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession]; previewLayer.frame = self.view.layer.bounds; [self.view.layer addSublayer:previewLayer]; [self.captureSession startRunning]; } #pragma mark AVCaptureMetadataOutputObjectsDelegate - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection { for(AVMetadataObject *metadataObject in metadataObjects) { AVMetadataMachineReadableCodeObject *readableObject = (AVMetadataMachineReadableCodeObject *)metadataObject; if([metadataObject.type isEqualToString:AVMetadataObjectTypeQRCode]) { NSLog(@"QR Code = %@", readableObject.stringValue); } else if ([metadataObject.type isEqualToString:AVMetadataObjectTypeEAN13Code]) { NSLog(@"EAN 13 = %@", readableObject.stringValue); } } } @end
Pitfalls
The issue I had with NSHipster’s sample code is the delegate method was not called at all. I quickly understood this was because the AVCaptureMetadataOutput must be configured to tell which metadata to recognise.
But, and this was not obvious to me, -[AVCaptureMetadataOuput setMetadataObjectTypes:] must be called after -[AVCaptureSession addOutput:]. Otherwise, the following message shows in the console:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[AVCaptureMetadataOutput setMetadataObjectTypes:] - unsupported type found. Use -availableMetadataObjectTypes.'
I did try to look at the output of -availableMetadataObjectTypes, and it returns an empty array.
Therefore, -addOuput: must be called before setMetadataObjectTypes. In retrospect, it makes sense: the output object must know it is linked to a video session to know which metadata it may provide.
10 replies on “Reading Barcodes on iOS 7”
Ah, this caught me out! I had a quick search on Google, and happily your answer came up — so thank you for that 🙂 I’m not sure I’d have worked it out myself, because I’ve not used the AVCapture stuff before (and really I’m just having a play with the new stuff because it’s something we might use in a project soon). I’m really glad to see it working.
Thanks once again 🙂
You’re welcome Adrian! I wrote it so people don’t spend an hour on these trivial details like I did. It’s very easy to use, after all.
Hello Renaud,
Thank you for this post. It was very helpful, although I’m running into problems with scanning QR codes. Have you managed to get the above code to recognise any QR codes? I’m having good successful with a range of the other AVMetadataObjectType(s) but no luck with AVMetadataObjectTypeQRCode on a range of QR code samples that successfully work with other QR code scanners off the app store.
Best,
– Ian
Hello Ian,
I actually only tested with a couple of QR codes from a magazine, but yes it worked for me. I think there are several reasons which could prevent scanning.
The first one is it takes a lot of processing power. However, I did hav a successful scan on an iPad 2 (slow device by today’s standards) with a full-screen capture. You may try to limit the scanning zone; it could help.
The second one is trying with better light conditions.
Hope it helps.
Hello Renaud,
Thank you for your reply.
That is very interesting. I tried on an iPhone 5 with various printouts and also the iMac 21″ monitor displaying the sample QR code displays on http://en.wikipedia.org/wiki/QR_code#Design – it’s interesting that other apps all decoded the QR glyphs fine but not my binary (basically the same as your code), which reads a range of other barcode types with no problems at all. I’ll keep investigating. It’s curious…
Best,
– Ian
Ian,
I’ve just tried with my iPhone 5 and the Wikipedia samples, and it works perfectly for me, except for the Version 40. But even the Version 10 works. (By the way, I could not scan with the iPhone Simulator).
Hello Renaud,
Thanks for confirmation on your end that it works. It is very strange that it doesn’t work for me on my iPhone 5 when scanning for QR codes, but that I have no problem with other barcode types. I’ll try it with a iPhone 4S and an iPad Gen 4 & Gen 2 – maybe I’ll have better luck (though it makes no sense.)
However, I am pleased that the SDK *does* work. Now I just need to figure out why it doesn’t work for me! (-;
Thanks for your help in testing the Wikipedia samples though. It’s greatly appreciated.
Best,
– Ian
Ah ha! I got it working. It was a silly mistake on my part. Although my delegate handled AVMetadataObjectTypeQRCode – I overlooked adding the QRCode type in setMetadataObjectTypes, when setting up the capture, like:
setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode,AVMetadataObjectTypeEAN13Code , etc …];
Thank you! Really appreciated your independent confirmation that it worked, and your code in your blog post to compare against my testbed!
Best,
– Ian
Hi,
Thank you for sharing your code.
I still got that error message though…
[AVCaptureMetadataOutput setMetadataObjectTypes:] – unsupported type found. Use -availableMetadataObjectTypes.’
Do you have any idea’s?
Kind regards,
Dider
Most probably, you’re calling the method too early. Please read the article again, I explain why it happens. If not, see the output of -availableMetadateObjectTypes as suggested; you might get extra info.