You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

170 lines
6.6 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import SwiftUI
import AVFoundation
import AudioToolbox
import Combine
// MARK: -
struct ScannerView: View {
@StateObject private var scannerViewModel = ScannerViewModel()
@State private var showPreviewPause = false
@State private var selectedScanningStyle: ScanningLineStyle = .modern
@State private var screenOrientation = UIDevice.current.orientation
@State private var previewLayer: AVCaptureVideoPreviewLayer?
@Environment(\.dismiss) private var dismiss
var body: some View {
ZStack {
//
if scannerViewModel.cameraAuthorizationStatus == .authorized {
//
CameraPreviewView(session: scannerViewModel.captureSession, previewLayer: $previewLayer)
.ignoresSafeArea()
//
ScanningOverlayView(
showPreviewPause: showPreviewPause,
selectedStyle: $selectedScanningStyle,
detectedCodesCount: scannerViewModel.detectedCodes.count,
onClose: { dismiss() }
)
//
if showPreviewPause && !scannerViewModel.detectedCodes.isEmpty {
CodePositionOverlay(
detectedCodes: scannerViewModel.detectedCodes,
previewLayer: previewLayer,
onCodeSelected: handleCodeSelection,
onRescan: resetToScanning
)
}
//
if showPreviewPause && scannerViewModel.detectedCodes.count == 1 {
TestAutoSelectButton(
detectedCode: scannerViewModel.detectedCodes[0],
onSelect: handleCodeSelection
)
}
} else {
// UI
CameraPermissionView(
authorizationStatus: scannerViewModel.cameraAuthorizationStatus,
onRequestPermission: {
scannerViewModel.refreshCameraPermission()
},
onOpenSettings: {
scannerViewModel.openSettings()
}
)
}
}
.onAppear {
//
if scannerViewModel.cameraAuthorizationStatus == .authorized {
scannerViewModel.startScanning()
}
}
.onDisappear {
scannerViewModel.stopScanning()
}
.alert("scan_error_title".localized, isPresented: $scannerViewModel.showAlert) {
Button("OK") { }
} message: {
Text("scan_error_message".localized)
}
.onReceive(scannerViewModel.$detectedCodes) { codes in
handleDetectedCodes(codes)
}
.onReceive(scannerViewModel.$cameraAuthorizationStatus) { status in
if status == .authorized {
logInfo("🎯 相机权限已授权,启动扫描", className: "ScannerView")
scannerViewModel.startScanning()
}
}
.onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
handleOrientationChange()
}
}
// MARK: -
private func handleDetectedCodes(_ codes: [DetectedCode]) {
guard !codes.isEmpty else { return }
logInfo("检测到条码数量: \(codes.count)", className: "ScannerView")
if codes.count == 1 {
logInfo("单个条码,准备自动选择", className: "ScannerView")
pauseForPreview()
autoSelectSingleCode(code: codes[0])
} else {
logInfo("多个条码,等待用户选择", className: "ScannerView")
pauseForPreview()
}
}
private func handleOrientationChange() {
screenOrientation = UIDevice.current.orientation
logInfo("Screen orientation changed to: \(screenOrientation.rawValue)", className: "ScannerView")
}
private func handleCodeSelection(_ selectedCode: DetectedCode) {
logInfo("🎯 ScannerView 收到条码选择回调", className: "ScannerView")
logInfo(" 选择的条码ID: \(selectedCode.id)", className: "ScannerView")
logInfo(" 选择的条码类型: \(selectedCode.type)", className: "ScannerView")
logInfo(" 选择的条码内容: \(selectedCode.content)", className: "ScannerView")
logInfo(" 选择的条码位置: \(selectedCode.bounds)", className: "ScannerView")
let formattedResult = "类型: \(selectedCode.type)\n内容: \(selectedCode.content)"
logInfo(" 格式化结果: \(formattedResult)", className: "ScannerView")
NotificationCenter.default.post(name: .scannerDidScanCode, object: formattedResult)
dismiss()
}
private func pauseForPreview() {
showPreviewPause = true
}
private func resetToScanning() {
logInfo("🔄 ScannerView 开始重置到扫描状态", className: "ScannerView")
// UI
showPreviewPause = false
//
scannerViewModel.resetDetection()
scannerViewModel.restartScanning()
//
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
logInfo("🔍 检查扫描会话状态", className: "ScannerView")
self.scannerViewModel.checkSessionStatus()
}
logInfo("✅ ScannerView 已重置到扫描状态", className: "ScannerView")
}
private func autoSelectSingleCode(code: DetectedCode) {
logInfo("开始自动选择定时器,条码类型: \(code.type)", className: "ScannerView")
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
guard self.showPreviewPause && self.scannerViewModel.detectedCodes.count == 1 else {
logInfo("条件不满足,取消自动选择", className: "ScannerView")
return
}
logInfo("条件满足,执行自动选择", className: "ScannerView")
self.handleCodeSelection(code)
}
}
}
#if DEBUG
struct ScannerView_Previews: PreviewProvider {
static var previews: some View {
ScannerView()
}
}
#endif