Coding the App
To code your app, begin by importing the AVFoundation library in the ScannerViewController.swift file:
import UIKit import AVFoundation
Next, create a protocol named ScannerViewControllerDelegate so that it can contain a method named barcodeObtained. When you scan a barcode, you'll call this method so that any View Controller that calls this View Controller (ScannerViewController) can obtain the barcode obtained:
//---to be implemented by the View Controller calling this View Controller--- protocol ScannerViewControllerDelegate { //---close the current View Controller and return the barcode obtained--- func barcodeObtained(viewController: ScannerViewController, data: String) }
In the ScannerViewController class, conform to the AVCaptureMetadataOutputObjectsDelegate protocol and then declare variables as shown in bold in the following code:
class ScannerViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate { //---delegate to handle the barcodeObtained method--- var delegate: ScannerViewControllerDelegate? var captureSession: AVCaptureSession! var device : AVCaptureDevice! var deviceInput: AVCaptureDeviceInput! var metadataOutput: AVCaptureMetadataOutput! var previewLayer : AVCaptureVideoPreviewLayer! var barcodeCapturedView : UIView! var audioPlayer:AVAudioPlayer!
You use an AVCaptureSession object to coordinate the flow of data from an AV input device, such as from an AVCaptureDevice object. An AVCaptureDeviceInput object is used to capture data from an AVCaptureDevice object. An AVCaptureMetadataOutput object intercepts metadata objects from an AVCaptureSession object so that you can forward it to a delegate for further processing. To preview the video captured by an AVCaptureSession object, you use an AVCaptureVideoPreviewLayer object.
You'll also use a UIView object that you can overlay on the video preview to show the outline of the detected barcode (see Figure 7). Finally, you use an AVAudioPlayer object to play a beep sound when a barcode has been scanned.
Figure 7 Displaying a rectangle around the scanned barcode.
Add the following code to the viewDidLoad() method:
override func viewDidLoad() { super.viewDidLoad() //---view to display a border around the captured barcode--- barcodeCapturedView = UIView() barcodeCapturedView.autoresizingMask = UIViewAutoresizing.FlexibleTopMargin | UIViewAutoresizing.FlexibleLeftMargin | UIViewAutoresizing.FlexibleRightMargin | UIViewAutoresizing.FlexibleBottomMargin //---draw a yellow border around the barcode scanned--- barcodeCapturedView.layer.borderColor = UIColor.yellowColor().CGColor barcodeCapturedView.layer.borderWidth = 5; self.view.addSubview(barcodeCapturedView) //---set up the AVCaptureSession together with the AVCaptureDevice and // AVCaptureDeviceInput--- captureSession = AVCaptureSession() device = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo) var error : NSError? deviceInput = AVCaptureDeviceInput.deviceInputWithDevice( device, error:&error) as AVCaptureDeviceInput if deviceInput != nil { captureSession.addInput(deviceInput) } else { println("Error: \(error)") } //---set up the delegate for the AVCaptureMetadataOutput so that you can // process the scanned barcode--- metadataOutput = AVCaptureMetadataOutput() metadataOutput.setMetadataObjectsDelegate(self, queue:dispatch_get_main_queue()) captureSession.addOutput(metadataOutput) metadataOutput.metadataObjectTypes = metadataOutput.availableMetadataObjectTypes //---the layer to preview the video capturing the barcode--- previewLayer = AVCaptureVideoPreviewLayer(session:captureSession) previewLayer.frame = self.view.bounds previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill self.view.layer.addSublayer(previewLayer) self.view.bringSubviewToFront(barcodeCapturedView) //---start scanning--- captureSession.startRunning() }
When the barcode is recognized, the AVCaptureMetadataOutput object fires the captureOutput:didOutputMetadataObjects:fromConnection: method.
Add the following method to the ScannerViewController class:
//---fired when the barcode is captured--- func captureOutput( captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!) { var barcodeCapturedRect = CGRectZero var barCodeObject : AVMetadataMachineReadableCodeObject var barcodeScanned = "" //---types of symbologies recognized--- var symbologies = [ AVMetadataObjectTypeUPCECode, AVMetadataObjectTypeCode39Code, AVMetadataObjectTypeCode39Mod43Code, AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode93Code, AVMetadataObjectTypeCode128Code, AVMetadataObjectTypePDF417Code, AVMetadataObjectTypeQRCode, AVMetadataObjectTypeAztecCode, AVMetadataObjectTypeInterleaved2of5Code, AVMetadataObjectTypeITF14Code, AVMetadataObjectTypeDataMatrixCode] //---loop through the metadata and see if they match any of the // supported symbologies--- for metadata in metadataObjects { for symbology in symbologies{ if metadata.type == symbology { //---get the screen coordinates of the barcode scanned--- barCodeObject = previewLayer.transformedMetadataObjectForMetadataObject( metadata as AVMetadataMachineReadableCodeObject) as AVMetadataMachineReadableCodeObject barcodeCapturedRect = barCodeObject.bounds; barcodeScanned = (metadata as AVMetadataMachineReadableCodeObject).stringValue //---play a beep sound--- playSound() //---outline the barcode that is detected--- barcodeCapturedView.frame = barcodeCapturedRect; //---stop the scanning--- captureSession.stopRunning() break; } } if barcodeScanned != "" { break; } else { barcodeScanned = "No barcode detected" } } //---close the window, return the barcode scanned, // and return to the previous View Controller--- delegate?.barcodeObtained(self, data: barcodeScanned) self.dismissViewControllerAnimated(true, completion: nil) }
When a barcode is scanned, we check whether it belongs to one of the supported symbologies. If so, we play a beep sound to alert the user and display a rectangle around the found barcode. Finally, we call the barcodeObtained method of the delegate and dismiss the View Controller.
Add the playSound() method to the ScannerViewController class:
//---play a beep sound--- func playSound() { var soundFilePath = NSBundle.mainBundle().pathForResource("beep", ofType:"wav") var fileURL = NSURL(string: soundFilePath!) var error: NSError? audioPlayer = AVAudioPlayer(contentsOfURL: fileURL, error: &error) audioPlayer.play() }
To use the ScannerViewController class, create an outlet for the Label view and add the boldfaced code below to the ViewController.swift file:
import UIKit class ViewController: UIViewController, ScannerViewControllerDelegate { @IBOutlet weak var lblBarcode: UILabel! var scannerVC: ScannerViewController! override func viewDidLoad() { super.viewDidLoad() } override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { scannerVC = segue.destinationViewController as ScannerViewController scannerVC.delegate = self } func barcodeObtained(viewController: ScannerViewController, data: String) { self.lblBarcode.text = data } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } }