|
//
// QRCodeScanner.swift
// QRView
//
// Created by FFIB on 2017/7/19.
// Copyright © 2017年 FFIB. All rights reserved.
//
import UIKit
import AVFoundation
class QRCodeScanner: NSObject {
var didDecodeCode: ((QRCodeScanResult) -> Void)?
var didDecodeFail: ((NSError) -> Void)?
fileprivate let captureSession = AVCaptureSession()
fileprivate let input: AVCaptureInput? = {
guard let device = AVCaptureDevice.default(for: AVMediaType.video) else { return nil }
return try? AVCaptureDeviceInput(device: device)
}()
fileprivate let output = AVCaptureMetadataOutput()
var previewLayer: AVCaptureVideoPreviewLayer?
fileprivate var avaliable = true
var scanRect: CGRect
init(scanRect: CGRect) {
self.scanRect = scanRect
super.init()
commonInit()
}
fileprivate func commonInit() {
#if !TARGET_INTERFACE_BUILDER && !((arch(i386) || arch(x86_64)))
switch AVCaptureDevice.authorizationStatus(for: AVMediaType.video) {
case .notDetermined:
initCaputue()
startCaputue()
case .authorized:
self.initCaputue()
self.startCaputue()
default:
let alert = UIAlertView(title: "拒绝访问", message: "请在设置-隐私-相机中允许访问相机",
delegate: nil, cancelButtonTitle: "确定")
alert.show()
self.avaliable = false
}
#endif
}
fileprivate func initCaputue() {
guard let input = self.input else { return }
self.captureSession.sessionPreset = AVCaptureSession.Preset.hd1920x1080
if self.captureSession.canAddInput(input) {
self.captureSession.addInput(input)
} else {
self.avaliable = false
}
if self.captureSession.canAddOutput(self.output) {
self.captureSession.addOutput(self.output)
self.output.metadataObjectTypes = [AVMetadataObject.ObjectType.qr]
self.output.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
} else {
self.avaliable = false
}
}
fileprivate func setScanArea() {
//设置扫描区域
let cropRect = CGRect(x: (self.scanRect.width - self.scanRect.width / 1.5) / 2,
y: (self.scanRect.height - self.scanRect.width / 1.5) / 2,
width: self.scanRect.width / 1.5,
height: self.scanRect.width / 1.5)
output.rectOfInterest = CGRect(x: cropRect.origin.y / scanRect.height,
y: cropRect.origin.x / scanRect.width,
width: cropRect.height / scanRect.height,
height: cropRect.width / scanRect.width)
}
fileprivate func startCaputue() {
guard avaliable && !captureSession.isRunning else {
print("QRCode camera not avaliable")
return
}
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
}
func startScan() {
setScanArea()
// captureSession.startRunning()
}
func stopScan() {
if captureSession.isRunning {
captureSession.stopRunning()
}
}
deinit {
NotificationCenter.default.removeObserver(self,
name: Notification.QRNotification.RestartNotification,
object: nil)
}
func cueSound() {
var soundId = SystemSoundID()
guard let path = Bundle.main.path(forResource: "di", ofType: "mp3") else { return }
let url = URL(fileURLWithPath: path)
AudioServicesCreateSystemSoundID(url as CFURL,
&soundId)
AudioServicesPlaySystemSound(soundId)
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate)
}
}
extension QRCodeScanner: AVCaptureMetadataOutputObjectsDelegate {
func metadataOutput(_ output: AVCaptureMetadataOutput,
didOutput metadataObjects: [AVMetadataObject],
from connection: AVCaptureConnection) {
guard let metadatas = metadataObjects as? [AVMetadataMachineReadableCodeObject],
let res = metadatas.first?.stringValue, let type = metadataObjects.first?.type.rawValue else {
let error = NSError.init(domain: "scan qr code failure",
code: 102,
userInfo: nil)
didDecodeFail!(error)
return
}
stopScan()
cueSound()
didDecodeCode!(QRCodeScanResult(metadataType: type, result: res))
}
}
|