d6aeede2bc
从桌面工程复制而来,作为本仓库的 Xuxucheck/ 子目录。 为控制仓库体积做了两处删减: - 移除 Frameworks/opencv2.xcframework 下的两个 visionOS 切片 (xros-arm64、xros-arm64-simulator,合计约 106 MB),并同步删掉该 xcframework 内 Info.plist 里对应的两条 AvailableLibraries 记录, 只保留 ios-arm64 与 ios-arm64_x86_64-simulator。 - 移除全部 .DS_Store 与 xcuserdata/。 已用 xcodebuild 从本目录完整编译验证:BUILD SUCCEEDED, ProcessXCFramework 以 --platform ios 成功选中框架,产物含 3699 个 cv:: 符号 (OpenCV 确实链入),嵌入 framework 3 个,与移除无用 pod 之后的状态一致。 仓库约 206 MB,其中 OpenCV 203 MB、其余源码与依赖约 3 MB。
158 lines
5.1 KiB
Swift
158 lines
5.1 KiB
Swift
//
|
|
// 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 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
|
|
|
|
public init(
|
|
codeTypes: [AVMetadataObject.ObjectType],
|
|
scanMode: ScanMode = .once,
|
|
manualSelect: Bool = false,
|
|
scanInterval: Double = 2.0,
|
|
showViewfinder: 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.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, parentView: self)
|
|
}
|
|
|
|
public func updateUIViewController(_ uiViewController: ScannerViewController, context: Context) {
|
|
uiViewController.parentView = self
|
|
uiViewController.updateViewController(
|
|
isTorchOn: isTorchOn,
|
|
isGalleryPresented: isGalleryPresented.wrappedValue,
|
|
isManualCapture: scanMode.isManual,
|
|
isManualSelect: manualSelect
|
|
)
|
|
}
|
|
|
|
}
|
|
|
|
@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
|