123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123 |
- 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()
- break
- case .authorized:
- self.initCaputue()
- self.startCaputue()
- break
- default:
- let alert = UIAlertView(title: "拒绝访问", message: "请在设置-隐私-相机中允许访问相机", delegate: nil, cancelButtonTitle: "确定")
- alert.show()
- self.avaliable = false
- break
- }
- #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()
- AudioServicesCreateSystemSoundID(URL.init(fileURLWithPath: Bundle.main.path(forResource: "di", ofType: "mp3")!) 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))
- }
- }
|