mbuchetics
9/9/2016 - 8:19 AM

QRCodeScannerViewController

QRCodeScannerViewController

//
//  QRCodeScannerViewController.swift
//
//  Created by Matthias Buchetics on 09/09/16.
//  Copyright © 2016 Matthias Buchetics. All rights reserved.
//

import AVFoundation
import UIKit

class QRCodeScannerViewController: UIViewController {

    var captureSession: AVCaptureSession?
    var previewLayer: AVCaptureVideoPreviewLayer!

    var didFindCode: ((code: String?) -> Void)? = nil

    // MARK: UIViewController

    override func viewDidLoad() {
        super.viewDidLoad()

        title = NSLocalizedString("QRCodeScannerViewController.title", value: "QR Code", comment: "")

        navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Cancel, target: self, action: #selector(cancel))

        view.backgroundColor = UIColor.blackColor()

        captureSession = createCaptureSession()
        setupPreviewLayer(captureSession)
    }

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)

        guard let captureSession = captureSession else {
            showFailureAlert()
            return
        }

        if !captureSession.running {
            captureSession.startRunning()
        }
    }

    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)

        if let captureSession = captureSession where captureSession.running {
            captureSession.stopRunning()
        }
    }

    override func prefersStatusBarHidden() -> Bool {
        return true
    }

    override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        return .Portrait
    }

    // MARK: Setup

    func createCaptureSession() -> AVCaptureSession? {
        let captureSession = AVCaptureSession()
        let videoCaptureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
        let videoInput: AVCaptureDeviceInput

        do {
            videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
        } catch {
            return nil
        }

        if captureSession.canAddInput(videoInput) {
            captureSession.addInput(videoInput)
        } else {
            return nil
        }

        let metadataOutput = AVCaptureMetadataOutput()

        if captureSession.canAddOutput(metadataOutput) {
            captureSession.addOutput(metadataOutput)

            metadataOutput.setMetadataObjectsDelegate(self, queue: dispatch_get_main_queue())
            metadataOutput.metadataObjectTypes = [AVMetadataObjectTypeQRCode]
        } else {
            return nil
        }

        return captureSession
    }

    func setupPreviewLayer(captureSession: AVCaptureSession?) {
        guard let captureSession = captureSession else { return }

        previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        previewLayer.frame = view.layer.bounds
        previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill

        view.layer.addSublayer(previewLayer)
    }

    // MARK: Actions

    func cancel() {
        didFindCode?(code: nil)
    }

    // MARK: Alert

    func showFailureAlert() {
        let title = NSLocalizedString("QRCodeScannerViewController.failureAlert.title", value: "Fehler", comment: "")
        let message = NSLocalizedString("QRCodeScannerViewController.failureAlert.message", value: "Ihr Gerät unterstützt das Scannen eines QR Codes leider nicht.", comment: "")

        let alertController = UIAlertController(title: title, message: message, preferredStyle: .Alert)
        alertController.addOK {
            self.didFindCode?(code: nil)
        }

        presentViewController(alertController, animated: true, completion: nil)
    }
}

// MARK: - AVCaptureMetadataOutputObjectsDelegate

extension QRCodeScannerViewController: AVCaptureMetadataOutputObjectsDelegate {

    func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!) {
        guard let captureSession = captureSession else { return }

        captureSession.stopRunning()

        if let metadataObject = metadataObjects.first {
            let readableObject = metadataObject as! AVMetadataMachineReadableCodeObject

            AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))

            let code = readableObject.stringValue
            print("found code: \(code)")

            didFindCode?(code: readableObject.stringValue)
        }
    }
}