-
-
Notifications
You must be signed in to change notification settings - Fork 331
Expand file tree
/
Copy pathCodeScanner.swift
More file actions
170 lines (146 loc) · 5.69 KB
/
CodeScanner.swift
File metadata and controls
170 lines (146 loc) · 5.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
//
// CodeScanner.swift
// https://github.com/twostraws/CodeScanner
//
// Created by Paul Hudson on 14/12/2021.
// Copyright © 2021 Paul Hudson. All rights reserved.
//
#if os(iOS)
import AVFoundation
import SwiftUI
/// An enum describing the ways CodeScannerView can hit scanning problems.
public enum ScanError: Error {
/// The camera could not be accessed.
case badInput
/// The camera was not capable of scanning the requested codes.
case badOutput
/// Initialization failed.
case initError(_ error: Error)
/// The camera permission is denied
case permissionDenied
}
/// The result from a successful scan: the string that was scanned, and also the type of data that was found.
/// The type is useful for times when you've asked to scan several different code types at the same time, because
/// it will report the exact code type that was found.
@available(macCatalyst 14.0, *)
public struct ScanResult {
/// The contents of the code.
public let string: String
/// 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 corner coordinates of the scanned code.
public let corners: [CGPoint]
}
/// The operating mode for CodeScannerView.
public enum ScanMode {
/// Scan exactly one code, then stop.
case once
/// Scan each code no more than once.
case oncePerCode
/// Keep scanning all codes until dismissed.
case continuous
/// Keep scanning all codes - except the ones from the ignored list - until dismissed.
case continuousExcept(ignoredList: Set<String>)
/// Scan only when capture button is tapped.
case manual
var isManual: Bool {
switch self {
case .manual:
return true
case .once, .oncePerCode, .continuous, .continuousExcept:
return false
}
}
}
/// 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`.
/// For testing inside the simulator, set the `simulatedData` property to some test data you want to send back.
@available(macCatalyst 14.0, *)
public struct CodeScannerView: UIViewControllerRepresentable {
public let codeTypes: [AVMetadataObject.ObjectType]
public let scanMode: ScanMode
public let manualSelect: Bool
public let scanInterval: Double
public let showViewfinder: Bool
public let useViewfinderAsRectOfInterest: Bool
public let requiresPhotoOutput: Bool
public var simulatedData = ""
public var shouldVibrateOnSuccess: Bool
public var isTorchOn: Bool
public var isPaused: Bool
public var isGalleryPresented: Binding<Bool>
public var videoCaptureDevice: AVCaptureDevice?
public var completion: (Result<ScanResult, ScanError>) -> Void
private(set) var currentViewfinderStyle: AnyScannerViewfinderStyle = .init(style: .default)
public init(
codeTypes: [AVMetadataObject.ObjectType],
scanMode: ScanMode = .once,
manualSelect: Bool = false,
scanInterval: Double = 2.0,
showViewfinder: Bool = false,
useViewfinderAsRectOfInterest: Bool = false,
requiresPhotoOutput: Bool = true,
simulatedData: String = "",
shouldVibrateOnSuccess: Bool = true,
isTorchOn: Bool = false,
isPaused: Bool = false,
isGalleryPresented: Binding<Bool> = .constant(false),
videoCaptureDevice: AVCaptureDevice? = AVCaptureDevice.bestForVideo,
completion: @escaping (Result<ScanResult, ScanError>) -> Void
) {
self.codeTypes = codeTypes
self.scanMode = scanMode
self.manualSelect = manualSelect
self.showViewfinder = showViewfinder
self.useViewfinderAsRectOfInterest = useViewfinderAsRectOfInterest
self.requiresPhotoOutput = requiresPhotoOutput
self.scanInterval = scanInterval
self.simulatedData = simulatedData
self.shouldVibrateOnSuccess = shouldVibrateOnSuccess
self.isTorchOn = isTorchOn
self.isPaused = isPaused
self.isGalleryPresented = isGalleryPresented
self.videoCaptureDevice = videoCaptureDevice
self.completion = completion
}
public func makeUIViewController(context: Context) -> ScannerViewController {
return ScannerViewController(
showViewfinder: showViewfinder,
useViewfinderAsRectOfInterest: useViewfinderAsRectOfInterest,
parentView: self
)
}
public func updateUIViewController(_ uiViewController: ScannerViewController, context: Context) {
uiViewController.parentView = self
uiViewController.updateViewController(
isTorchOn: isTorchOn,
isGalleryPresented: isGalleryPresented.wrappedValue,
isManualCapture: scanMode.isManual,
isManualSelect: manualSelect
)
}
public func viewfinderStyle<S>(_ style: S) -> Self where S: ScannerViewfinderStyle {
var copy = self
copy.currentViewfinderStyle = .init(style: style)
return copy
}
}
@available(macCatalyst 14.0, *)
extension CodeScannerView {
@available(*, deprecated, renamed: "requiresPhotoOutput")
public var requirePhotoOutput: Bool {
requiresPhotoOutput
}
}
@available(macCatalyst 14.0, *)
struct CodeScannerView_Previews: PreviewProvider {
static var previews: some View {
CodeScannerView(codeTypes: [.qr]) { result in
// do nothing
}
}
}
#endif