diff --git a/Sources/CodeScanner/CodeScanner.swift b/Sources/CodeScanner/CodeScanner.swift index 85b7994..1fb05ec 100644 --- a/Sources/CodeScanner/CodeScanner.swift +++ b/Sources/CodeScanner/CodeScanner.swift @@ -35,12 +35,20 @@ public struct ScanResult { /// The type of code that was matched. public let type: AVMetadataObject.ObjectType - - /// The image of the code that was matched + + /// The source of the image where the code was scanned. + public let source: ScanSource + + /// The image of the code that was matched. public let image: UIImage? /// The corner coordinates of the scanned code. public let corners: [CGPoint] + + /// The bounding rectangle that contains the code that was scanned. + /// When the source is camera the rect is within the local coordinate space of the scanner view. + /// When the source is gallery the rect is in image coordinates. + public let boundingRect: CGRect } /// The operating mode for CodeScannerView. @@ -70,6 +78,15 @@ public enum ScanMode { } } +/// The image source of the ScanResult. +public enum ScanSource { + /// The image was captured by the camera represented by the provided or default `AVCaptureDevice`. + case camera + + /// The image was provided by the user's photo gallery using `UIImagePickerController`. + case gallery +} + /// A SwiftUI view that is able to scan barcodes, QR codes, and more, and send back what was found. /// To use, set `codeTypes` to be an array of things to scan for, e.g. `[.qr]`, and set `completion` to /// a closure that will be called when scanning has finished. This will be sent the string that was detected or a `ScanError`. diff --git a/Sources/CodeScanner/ScannerViewController.swift b/Sources/CodeScanner/ScannerViewController.swift index 292194a..83d5bb3 100644 --- a/Sources/CodeScanner/ScannerViewController.swift +++ b/Sources/CodeScanner/ScannerViewController.swift @@ -58,6 +58,8 @@ extension CodeScannerView { } #if targetEnvironment(simulator) + var previewLayer: AVCaptureVideoPreviewLayer? + override public func loadView() { view = UIView() view.isUserInteractionEnabled = true @@ -96,7 +98,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, image: nil, corners: [] + type: parentView.codeTypes.first ?? .qr, source: .gallery, image: nil, corners: [], boundingRect: .zero )) } @@ -374,11 +376,8 @@ extension CodeScannerView { #endif func updateViewController(isTorchOn: Bool, isGalleryPresented: Bool, isManualCapture: Bool, isManualSelect: Bool) { - guard let videoCaptureDevice = parentView.videoCaptureDevice ?? fallbackVideoCaptureDevice else { - return - } - - if videoCaptureDevice.hasTorch { + if let videoCaptureDevice = parentView.videoCaptureDevice ?? fallbackVideoCaptureDevice, + videoCaptureDevice.hasTorch { try? videoCaptureDevice.lockForConfiguration() videoCaptureDevice.torchMode = isTorchOn ? .on : .off videoCaptureDevice.unlockForConfiguration() @@ -445,6 +444,7 @@ extension CodeScannerView.ScannerViewController: AVCaptureMetadataOutputObjectsD !didFinishScanning, !isCapturing, let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject, + let boundingRect = previewLayer?.transformedMetadataObject(for: metadataObject)?.bounds, let stringValue = readableObject.stringValue else { return @@ -452,7 +452,7 @@ extension CodeScannerView.ScannerViewController: AVCaptureMetadataOutputObjectsD handler = { [weak self] image in guard let self else { return } - let result = ScanResult(string: stringValue, type: readableObject.type, image: image, corners: readableObject.corners) + let result = ScanResult(string: stringValue, type: readableObject.type, source: .camera, image: image, corners: readableObject.corners, boundingRect: boundingRect) switch parentView.scanMode { case .once: @@ -530,7 +530,7 @@ extension CodeScannerView.ScannerViewController: UIImagePickerControllerDelegate feature.topLeft ] - let result = ScanResult(string: qrCodeLink, type: .qr, image: qrcodeImg, corners: corners) + let result = ScanResult(string: qrCodeLink, type: .qr, source: .gallery, image: qrcodeImg, corners: corners, boundingRect: feature.bounds) found(result) } }