Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Sources/CodeScanner/CodeScanner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
78 changes: 52 additions & 26 deletions Sources/CodeScanner/ScannerViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>()
var didFinishScanning = false
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
))
}

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}
}

Expand Down Expand Up @@ -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)
}
}