@@ -12,21 +12,25 @@ import UIKit
12
12
@available ( macCatalyst 14 . 0 , * )
13
13
extension CodeScannerView {
14
14
15
- public class ScannerViewController : UIViewController , UIImagePickerControllerDelegate , UINavigationControllerDelegate {
15
+ public class ScannerViewController : UIViewController , UIImagePickerControllerDelegate , UINavigationControllerDelegate , AVCaptureMetadataOutputObjectsDelegate {
16
16
17
- var delegate : ScannerCoordinator ?
17
+ var parentView : CodeScannerView !
18
+ var codesFound = Set < String > ( )
19
+ var didFinishScanning = false
20
+ var lastTime = Date ( timeIntervalSince1970: 0 )
18
21
private let showViewfinder : Bool
19
22
20
23
private var isGalleryShowing : Bool = false {
21
24
didSet {
22
25
// Update binding
23
- if delegate ? . parent . isGalleryPresented. wrappedValue != isGalleryShowing {
24
- delegate ? . parent . isGalleryPresented. wrappedValue = isGalleryShowing
26
+ if parentView . isGalleryPresented. wrappedValue != isGalleryShowing {
27
+ parentView . isGalleryPresented. wrappedValue = isGalleryShowing
25
28
}
26
29
}
27
30
}
28
31
29
- public init ( showViewfinder: Bool = false ) {
32
+ public init ( showViewfinder: Bool = false , parentView: CodeScannerView ) {
33
+ self . parentView = parentView
30
34
self . showViewfinder = showViewfinder
31
35
super. init ( nibName: nil , bundle: nil )
32
36
}
@@ -62,10 +66,10 @@ extension CodeScannerView {
62
66
}
63
67
64
68
if qrCodeLink == " " {
65
- delegate ? . didFail ( reason: . badOutput)
69
+ didFail ( reason: . badOutput)
66
70
} else {
67
71
let result = ScanResult ( string: qrCodeLink, type: . qr)
68
- delegate ? . found ( result)
72
+ found ( result)
69
73
}
70
74
} else {
71
75
print ( " Something went wrong " )
@@ -203,7 +207,7 @@ extension CodeScannerView {
203
207
view. layer. addSublayer ( previewLayer)
204
208
addviewfinder ( )
205
209
206
- delegate ? . reset ( )
210
+ reset ( )
207
211
208
212
if ( captureSession. isRunning == false ) {
209
213
DispatchQueue . global ( qos: . userInteractive) . async {
@@ -217,7 +221,7 @@ extension CodeScannerView {
217
221
case . restricted:
218
222
break
219
223
case . denied:
220
- self . delegate ? . didFail ( reason: . permissionDenied)
224
+ self . didFail ( reason: . permissionDenied)
221
225
case . notDetermined:
222
226
self . requestCameraAccess {
223
227
self . setupCaptureDevice ( )
@@ -237,7 +241,7 @@ extension CodeScannerView {
237
241
private func requestCameraAccess( completion: ( ( ) -> Void ) ? ) {
238
242
AVCaptureDevice . requestAccess ( for: . video) { [ weak self] status in
239
243
guard status else {
240
- self ? . delegate ? . didFail ( reason: . permissionDenied)
244
+ self ? . didFail ( reason: . permissionDenied)
241
245
return
242
246
}
243
247
completion ? ( )
@@ -260,7 +264,7 @@ extension CodeScannerView {
260
264
private func setupCaptureDevice( ) {
261
265
captureSession = AVCaptureSession ( )
262
266
263
- guard let videoCaptureDevice = delegate ? . parent . videoCaptureDevice ?? fallbackVideoCaptureDevice else {
267
+ guard let videoCaptureDevice = parentView . videoCaptureDevice ?? fallbackVideoCaptureDevice else {
264
268
return
265
269
}
266
270
@@ -269,14 +273,14 @@ extension CodeScannerView {
269
273
do {
270
274
videoInput = try AVCaptureDeviceInput ( device: videoCaptureDevice)
271
275
} catch {
272
- delegate ? . didFail ( reason: . initError( error) )
276
+ didFail ( reason: . initError( error) )
273
277
return
274
278
}
275
279
276
280
if ( captureSession!. canAddInput ( videoInput) ) {
277
281
captureSession!. addInput ( videoInput)
278
282
} else {
279
- delegate ? . didFail ( reason: . badInput)
283
+ didFail ( reason: . badInput)
280
284
return
281
285
}
282
286
@@ -285,10 +289,10 @@ extension CodeScannerView {
285
289
if ( captureSession!. canAddOutput ( metadataOutput) ) {
286
290
captureSession!. addOutput ( metadataOutput)
287
291
288
- metadataOutput. setMetadataObjectsDelegate ( delegate , queue: DispatchQueue . main)
289
- metadataOutput. metadataObjectTypes = delegate ? . parent . codeTypes
292
+ metadataOutput. setMetadataObjectsDelegate ( self , queue: DispatchQueue . main)
293
+ metadataOutput. metadataObjectTypes = parentView . codeTypes
290
294
} else {
291
- delegate ? . didFail ( reason: . badOutput)
295
+ didFail ( reason: . badOutput)
292
296
return
293
297
}
294
298
}
@@ -330,7 +334,7 @@ extension CodeScannerView {
330
334
public override func touchesBegan( _ touches: Set < UITouch > , with event: UIEvent ? ) {
331
335
guard touches. first? . view == view,
332
336
let touchPoint = touches. first,
333
- let device = delegate ? . parent . videoCaptureDevice ?? fallbackVideoCaptureDevice,
337
+ let device = parentView . videoCaptureDevice ?? fallbackVideoCaptureDevice,
334
338
device. isFocusPointOfInterestSupported
335
339
else { return }
336
340
@@ -355,7 +359,7 @@ extension CodeScannerView {
355
359
}
356
360
357
361
@objc func manualCapturePressed( _ sender: Any ? ) {
358
- self . delegate ? . readyManualCapture ( )
362
+ self . readyManualCapture ( )
359
363
}
360
364
361
365
func showManualCaptureButton( _ isManualCapture: Bool ) {
@@ -408,5 +412,72 @@ extension CodeScannerView {
408
412
#endif
409
413
}
410
414
415
+ public func reset( ) {
416
+ codesFound. removeAll ( )
417
+ didFinishScanning = false
418
+ lastTime = Date ( timeIntervalSince1970: 0 )
419
+ }
420
+
421
+ public func readyManualCapture( ) {
422
+ guard parentView. scanMode == . manual else { return }
423
+ self . reset ( )
424
+ lastTime = Date ( )
425
+ }
426
+
427
+ public func metadataOutput( _ output: AVCaptureMetadataOutput , didOutput metadataObjects: [ AVMetadataObject ] , from connection: AVCaptureConnection ) {
428
+ if let metadataObject = metadataObjects. first {
429
+ guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else { return }
430
+ guard let stringValue = readableObject. stringValue else { return }
431
+ guard didFinishScanning == false else { return }
432
+ let result = ScanResult ( string: stringValue, type: readableObject. type)
433
+
434
+ switch parentView. scanMode {
435
+ case . once:
436
+ found ( result)
437
+ // make sure we only trigger scan once per use
438
+ didFinishScanning = true
439
+
440
+ case . manual:
441
+ if !didFinishScanning, isWithinManualCaptureInterval ( ) {
442
+ found ( result)
443
+ didFinishScanning = true
444
+ }
445
+
446
+ case . oncePerCode:
447
+ if !codesFound. contains ( stringValue) {
448
+ codesFound. insert ( stringValue)
449
+ found ( result)
450
+ }
451
+
452
+ case . continuous:
453
+ if isPastScanInterval ( ) {
454
+ found ( result)
455
+ }
456
+ }
457
+ }
458
+ }
459
+
460
+ func isPastScanInterval( ) -> Bool {
461
+ Date ( ) . timeIntervalSince ( lastTime) >= parentView. scanInterval
462
+ }
463
+
464
+ func isWithinManualCaptureInterval( ) -> Bool {
465
+ Date ( ) . timeIntervalSince ( lastTime) <= 0.5
466
+ }
467
+
468
+ func found( _ result: ScanResult ) {
469
+ lastTime = Date ( )
470
+
471
+ if parentView. shouldVibrateOnSuccess {
472
+ AudioServicesPlaySystemSound ( SystemSoundID ( kSystemSoundID_Vibrate) )
473
+ }
474
+
475
+ parentView. completion ( . success( result) )
476
+ }
477
+
478
+ func didFail( reason: ScanError ) {
479
+ parentView. completion ( . failure( reason) )
480
+ }
481
+
411
482
}
412
483
}
0 commit comments