diff --git a/Sources/CodeScanner/CodeScanner.swift b/Sources/CodeScanner/CodeScanner.swift index ca52dfc..c89721f 100644 --- a/Sources/CodeScanner/CodeScanner.swift +++ b/Sources/CodeScanner/CodeScanner.swift @@ -34,6 +34,9 @@ public struct ScanResult { /// The type of code that was matched. public let type: AVMetadataObject.ObjectType + + /// The image of the code that was matched + public let image: UIImage? } /// The operating mode for CodeScannerView. diff --git a/Sources/CodeScanner/ScannerViewController.swift b/Sources/CodeScanner/ScannerViewController.swift index c34ba56..9d3c5e0 100644 --- a/Sources/CodeScanner/ScannerViewController.swift +++ b/Sources/CodeScanner/ScannerViewController.swift @@ -13,7 +13,9 @@ import UIKit extension CodeScannerView { public class ScannerViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, AVCaptureMetadataOutputObjectsDelegate { - + private let photoOutput = AVCapturePhotoOutput() + private var isCapturing = false + private var handler: ((UIImage) -> Void)? var parentView: CodeScannerView! var codesFound = Set() var didFinishScanning = false @@ -68,7 +70,7 @@ extension CodeScannerView { if qrCodeLink == "" { didFail(reason: .badOutput) } else { - let result = ScanResult(string: qrCodeLink, type: .qr) + let result = ScanResult(string: qrCodeLink, type: .qr, image: qrcodeImg) found(result) } } else { @@ -122,7 +124,7 @@ extension CodeScannerView { // Send back their simulated data, as if it was one of the types they were scanning for found(ScanResult( string: parentView.simulatedData, - type: parentView.codeTypes.first ?? .qr + type: parentView.codeTypes.first ?? .qr, image: nil )) } @@ -280,12 +282,11 @@ extension CodeScannerView { didFail(reason: .badInput) return } - let metadataOutput = AVCaptureMetadataOutput() if (captureSession!.canAddOutput(metadataOutput)) { captureSession!.addOutput(metadataOutput) - + captureSession?.addOutput(photoOutput) metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main) metadataOutput.metadataObjectTypes = parentView.codeTypes } else { @@ -425,32 +426,41 @@ extension CodeScannerView { if let metadataObject = metadataObjects.first { guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else { return } guard let stringValue = readableObject.stringValue else { return } + guard didFinishScanning == false else { return } - let result = ScanResult(string: stringValue, type: readableObject.type) - - switch parentView.scanMode { - case .once: - found(result) - // make sure we only trigger scan once per use - didFinishScanning = true - - case .manual: - if !didFinishScanning, isWithinManualCaptureInterval() { - found(result) - didFinishScanning = true - } + + let photoSettings = AVCapturePhotoSettings() + guard !isCapturing else { return } + isCapturing = true + + handler = { [self] image in + let result = ScanResult(string: stringValue, type: readableObject.type, image: image) - case .oncePerCode: - if !codesFound.contains(stringValue) { - codesFound.insert(stringValue) - found(result) - } - - case .continuous: - if isPastScanInterval() { + switch parentView.scanMode { + case .once: found(result) + // make sure we only trigger scan once per use + didFinishScanning = true + + case .manual: + if !didFinishScanning, isWithinManualCaptureInterval() { + found(result) + didFinishScanning = true + } + + case .oncePerCode: + if !codesFound.contains(stringValue) { + codesFound.insert(stringValue) + found(result) + } + + case .continuous: + if isPastScanInterval() { + found(result) + } } } + photoOutput.capturePhoto(with: photoSettings, delegate: self) } } @@ -478,3 +488,19 @@ extension CodeScannerView { } } + +@available(macCatalyst 14.0, *) +extension CodeScannerView.ScannerViewController: AVCapturePhotoCaptureDelegate { + public func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) { + isCapturing = false + guard let imageData = photo.fileDataRepresentation() else { + print("Error while generating image from photo capture data."); + return + } + guard let qrImage = UIImage(data: imageData) else { + print("Unable to generate UIImage from image data."); + return + } + handler?(qrImage) + } +}