Having discovered that one of my iOS applications just maybe needed to be able to read a QR code I was pleased to discover that from iOS 7.0 onwards it is possible to do so using the AV Foundation framework.
There was little bit of trial and error involved in working out how to do set up but after that it “just worked” as they say
1.0 A Session
The first thing we need is a session
let session = AVCaptureSession()
2.0 An Input
An AVCaptureSession
needs an input, an instance of AVCaptureInput
, from which to capture things.
In our case we need the input from a camera which, in the context of the AV Foundation framework, is a kind of AVCaptureDevice
associaed with the media type
AVMediaTypeVideo
We can obtain the default device for capturing a given media type by calling the AVCaptureDevice
method
class func defaultDeviceWithMediaType(_ mediaType: String!) -> AVCaptureDevice!
or, alternatively, all the devices capable of capturing a given media type by calling the AVCaptureDevice
method
class func devicesWithMediaType(_ mediaType: String!) -> [AnyObject]!
Using the first method we can do
...
if let device = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
{
...
}
...
On an iPad or an iPhone running iOS 9.0 this returns the ‘back camera’.
If we want to ensure that we get the ‘back camera’, rather than just hoping we do, we can do
...
if let objects = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo)
{
for device in objects
{
if device.position == .Back
{
...
}
}
}
...
Once we have our desired device we can contruct an instance of AVCaptureDeviceInput
which is a sub-class of AVCaptureInput
let input = try AVCaptureDeviceInput(device:device)
amd then add it to the session
session.addInput(input)
3,0 An Output
To identify the QR code(s) if any, in the input from the camera we need an instance of the class AVCaptureMetadataOutput
.
let output = AVCaptureMetadataOutput()
We can then add this to the session as an output.
session.addOutput(output)
An AVCaptureMetadataOutput
object is capable of recognizing more than just QR codes.
To find out exactly what we can access the availableMetadataObjectTypes
property.
On an iPad running iOS 9.0 the following code
...
for o in output.availableMetadataObjectTypes
{
print(o)
}
...
produces this output
...
org.iso.Aztec
org.iso.Code128
org.iso.Code39
org.iso.Code39Mod43
com.intermec.Code93
org.iso.DataMatrix
org.gs1.EAN-13
org.gs1.EAN-8
org.ansi.Interleaved2of5
org.gs1.ITF14
org.iso.PDF417
org.iso.QRCode
org.gs1.UPC-E
face
...
and on an iPhone running iOS 9.0 this output
...
face
org.iso.Aztec
org.iso.Code128
org.iso.Code39
org.iso.Code39Mod43
com.intermec.Code93
org.iso.DataMatrix
org.gs1.EAN-13
org.gs1.EAN-8
org.ansi.Interleaved2of5
org.gs1.ITF14
org.iso.PDF417
org.iso.QRCode
org.gs1.UPC-E
...
To specify that we are only interested in QR codes we can set the metadataObjectTypes
property.
output.metadataObjectTypes = [AVMetadataObjectTypeQRCode]
Note
In the AVCaptureMetadataOutput
case the order in which the input and output are added is significant.
If it is added before the camera input then the availableMetadataTypes
property is simply an empty array and attempting to set the metadataTypes
property will result in an NSInvalidArgumentException
being thrown.
4.0 A Delegate
An AVCaptureMetadataOutput
object passes metadata objects obtained from the input to a delegate which is required to implement the AVCaptureMetadataOutputObjectsDelegate
protocol.
The AVCaptureMetadataOutputObjectsDelegate
protocol defines a single method
optional func captureOutput(
_
captureOutput:
AVCaptureOutput!,
didOutputMetadataObjects
metadataObjects:
[AnyObject]!,
fromConnection
connection:
AVCaptureConnection!)
Each element of the array passed as the metadataObjects
argument is an instance of a sub-class of the class AVMetadataObect
.
In the case of a QR code the element will be an instance of the class AVMetadataMachineReadableCodeObject
.
The value of the stringValue
property of an instance of AVMetadataMachineReadableCodeObject
is a human readable version of, in this case, the QR code.
func captureOutput(
captureOutput:
AVCaptureOutput!,
didOutputMetadataObjects
metadataObjects: [AnyObject]!,
fromConnection
connection:
AVCaptureConnection!)
{
for metadataObject in metadataObjects
{
if metadataObject.type == AVMetadataObjectTypeQRCode
{
let value = metadataObject.stringValue
...
}
}
}
The delegate for an AVCaptureMetadataOutput
object can be set using the method
public func setMetadataObjectsDelegate(
objectsDelegate:
AVCaptureMetadataOutputObjectsDelegate!,
queue
objectsCallbackQueue:
dispatch_queue_t!)
The queue
argument specifies the dispatch queue on which the delegate's method will be invoked.
Copyright (c) 2015 By Simon Lewis. All Rights Reserved.
Unauthorized use and/or duplication of this material without express and written permission from this blog's author and owner Simon Lewis is strictly prohibited.
Excerpts and links may be used, provided that full and clear credit is given to Simon Lewis and justanapplication.wordpress.com with appropriate and specific direction to the original content.