QRScannerView.swift 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. //
  2. // QRScannerView.swift
  3. // Gaspol
  4. //
  5. // Created by Qindi on 19/05/22.
  6. //
  7. import Foundation
  8. import UIKit
  9. import AVFoundation
  10. /// Delegate callback for the QRScannerView.
  11. protocol QRScannerViewDelegate: AnyObject {
  12. func qrScanningDidFail()
  13. func qrScanningSucceededWithCode(_ str: String?)
  14. func qrScanningDidStop()
  15. }
  16. class QRScannerView: UIView {
  17. weak var delegate: QRScannerViewDelegate?
  18. /// capture settion which allows us to start and stop scanning.
  19. var captureSession: AVCaptureSession?
  20. // Init methods..
  21. required init?(coder aDecoder: NSCoder) {
  22. super.init(coder: aDecoder)
  23. doInitialSetup()
  24. }
  25. override init(frame: CGRect) {
  26. super.init(frame: frame)
  27. doInitialSetup()
  28. }
  29. //MARK: overriding the layerClass to return `AVCaptureVideoPreviewLayer`.
  30. override class var layerClass: AnyClass {
  31. return AVCaptureVideoPreviewLayer.self
  32. }
  33. override var layer: AVCaptureVideoPreviewLayer {
  34. return super.layer as! AVCaptureVideoPreviewLayer
  35. }
  36. }
  37. extension QRScannerView {
  38. var isRunning: Bool {
  39. return captureSession?.isRunning ?? false
  40. }
  41. func startScanning() {
  42. captureSession?.startRunning()
  43. }
  44. func stopScanning() {
  45. captureSession?.stopRunning()
  46. delegate?.qrScanningDidStop()
  47. }
  48. /// Does the initial setup for captureSession
  49. private func doInitialSetup() {
  50. clipsToBounds = true
  51. captureSession = AVCaptureSession()
  52. guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else { return }
  53. let videoInput: AVCaptureDeviceInput
  54. do {
  55. videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
  56. } catch let error {
  57. //print(error)
  58. return
  59. }
  60. if (captureSession?.canAddInput(videoInput) ?? false) {
  61. captureSession?.addInput(videoInput)
  62. } else {
  63. scanningDidFail()
  64. return
  65. }
  66. let metadataOutput = AVCaptureMetadataOutput()
  67. if (captureSession?.canAddOutput(metadataOutput) ?? false) {
  68. captureSession?.addOutput(metadataOutput)
  69. metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
  70. metadataOutput.metadataObjectTypes = [.qr, .ean8, .ean13, .pdf417]
  71. } else {
  72. scanningDidFail()
  73. return
  74. }
  75. self.layer.session = captureSession
  76. self.layer.videoGravity = .resizeAspectFill
  77. captureSession?.startRunning()
  78. }
  79. func scanningDidFail() {
  80. delegate?.qrScanningDidFail()
  81. captureSession = nil
  82. }
  83. func found(code: String) {
  84. delegate?.qrScanningSucceededWithCode(code)
  85. }
  86. }
  87. extension QRScannerView: AVCaptureMetadataOutputObjectsDelegate {
  88. func metadataOutput(_ output: AVCaptureMetadataOutput,
  89. didOutput metadataObjects: [AVMetadataObject],
  90. from connection: AVCaptureConnection) {
  91. stopScanning()
  92. if let metadataObject = metadataObjects.first {
  93. guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else { return }
  94. guard let stringValue = readableObject.stringValue else { return }
  95. AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
  96. found(code: stringValue)
  97. }
  98. }
  99. }