@ -14,6 +14,7 @@ class ScannerViewModel: NSObject, ObservableObject, AVCaptureMetadataOutputObjec
var captureSession : AVCaptureSession !
var captureSession : AVCaptureSession !
private var metadataOutput : AVCaptureMetadataOutput ?
private var metadataOutput : AVCaptureMetadataOutput ?
private var videoDevice : AVCaptureDevice ?
private var videoDevice : AVCaptureDevice ?
private var isProcessingDetection = false // 添 加 处 理 状 态 标 记
override init ( ) {
override init ( ) {
super . init ( )
super . init ( )
@ -155,24 +156,80 @@ class ScannerViewModel: NSObject, ObservableObject, AVCaptureMetadataOutputObjec
func stopScanning ( ) {
func stopScanning ( ) {
logInfo ( " 🔄 停止扫描 " , className : " ScannerViewModel " )
logInfo ( " 🔄 停止扫描 " , className : " ScannerViewModel " )
// 立 即 设 置 处 理 标 记 , 防 止 新 的 检 测 被 处 理
isProcessingDetection = true
// 检 查 会 话 是 否 在 运 行
// 检 查 会 话 是 否 在 运 行
if captureSession ? . isRunning = = true {
if captureSession ? . isRunning = = true {
// 立 即 停 止 扫 描 会 话
captureSession ? . stopRunning ( )
logInfo ( " ✅ 扫描会话已停止 " , className : " ScannerViewModel " )
} else {
logInfo ( " ℹ ️ 扫描会话已经停止" , className : " ScannerViewModel " )
}
}
// / 暂 停 相 机 功 能 ( 用 于 预 览 暂 停 状 态 )
func pauseCamera ( ) {
logInfo ( " ⏸️ 暂停相机功能 " , className : " ScannerViewModel " )
// 设 置 处 理 标 记
isProcessingDetection = true
// 停 止 会 话 ( 但 保 留 配 置 )
if captureSession ? . isRunning = = true {
captureSession ? . stopRunning ( )
logInfo ( " ✅ 相机会话已暂停 " , className : " ScannerViewModel " )
} else {
logInfo ( " ℹ ️ 相机会话已经停止" , className : " ScannerViewModel " )
}
}
// / 恢 复 相 机 功 能 ( 用 于 恢 复 扫 描 )
func resumeCamera ( ) {
logInfo ( " ▶️ 恢复相机功能 " , className : " ScannerViewModel " )
// 检 查 相 机 权 限
guard cameraAuthorizationStatus = = . authorized else {
logWarning ( " ❌ 相机权限未授权,无法恢复相机 " , className : " ScannerViewModel " )
return
}
// 检 查 会 话 配 置
if captureSession = = nil || captureSession . inputs . isEmpty || captureSession . outputs . isEmpty {
logInfo ( " 🔄 重新设置相机会话 " , className : " ScannerViewModel " )
setupCaptureSession ( )
}
// 重 置 处 理 标 记
isProcessingDetection = false
// 启 动 相 机 会 话
if captureSession ? . isRunning != true {
logInfo ( " 🚀 启动相机会话 " , className : " ScannerViewModel " )
DispatchQueue . global ( qos : . userInitiated ) . async { [ weak self ] in
DispatchQueue . global ( qos : . userInitiated ) . async { [ weak self ] in
self ? . captureSession ? . stopRunning ( )
self ? . captureSession ? . st art Running( )
DispatchQueue . main . async {
DispatchQueue . main . async {
logInfo ( " ✅ 扫描会话已停止 " , className : " ScannerViewModel " )
if self ? . captureSession ? . isRunning = = true {
logInfo ( " ✅ 相机会话启动成功 " , className : " ScannerViewModel " )
} else {
logWarning ( " ⚠️ 相机会话启动失败 " , className : " ScannerViewModel " )
}
}
}
}
}
} else {
logInfo ( " ℹ ️ 扫描会话已经停止" , className : " ScannerViewModel " )
}
}
logInfo ( " ✅ 相机功能已恢复 " , className : " ScannerViewModel " )
}
}
func resetDetection ( ) {
func resetDetection ( ) {
DispatchQueue . main . async {
DispatchQueue . main . async {
logInfo ( " 🔄 重置检测状态,清空 detectedCodes " , className : " ScannerViewModel " )
logInfo ( " 🔄 重置检测状态,清空 detectedCodes " , className : " ScannerViewModel " )
self . detectedCodes = [ ]
self . detectedCodes = [ ]
self . isProcessingDetection = false // 重 置 处 理 标 记
}
}
}
}
@ -193,46 +250,29 @@ class ScannerViewModel: NSObject, ObservableObject, AVCaptureMetadataOutputObjec
func restartScanning ( ) {
func restartScanning ( ) {
logInfo ( " 🔄 重新开始扫描 " , className : " ScannerViewModel " )
logInfo ( " 🔄 重新开始扫描 " , className : " ScannerViewModel " )
// 确保 在 主 线 程 执 行 U I 相 关 操 作
// 先停 止 当 前 会 话
DispatchQueue . main . async { [ weak self ] in
if captureSession ? . isRunning = = true {
guard let self = self else { return }
logInfo ( " 🔄 停止当前运行的扫描会话 " , className : " ScannerViewModel " )
captureSession ? . stopRunning ( )
// 先 停 止 当 前 会 话
}
if self . captureSession ? . isRunning = = true {
logInfo ( " 🔄 停止当前运行的扫描会话 " , className : " ScannerViewModel " )
// 重 置 检 测 状 态
self . captureSession ? . stopRunning ( )
resetDetection ( )
}
// 延 迟 后 重 新 启 动 会 话
// 重 置 检 测 状 态
DispatchQueue . main . asyncAfter ( deadline : . now ( ) + 0.3 ) {
self . detectedCodes = [ ]
logInfo ( " 🔄 准备重新启动扫描会话 " , className : " ScannerViewModel " )
// 延 迟 后 重 新 启 动 会 话
// 在 后 台 线 程 启 动 会 话
DispatchQueue . main . asyncAfter ( deadline : . now ( ) + 0.5 ) {
DispatchQueue . global ( qos : . userInitiated ) . async { [ weak self ] in
self ? . captureSession ? . startRunning ( )
logInfo ( " 🔄 准备重新启动扫描会话 " , className : " ScannerViewModel " )
// 在 后 台 线 程 启 动 会 话
// 检 查 会 话 状 态
DispatchQueue . global ( qos : . userInitiated ) . async {
DispatchQueue . main . asyncAfter ( deadline : . now ( ) + 0.2 ) {
self . captureSession ? . startRunning ( )
if self ? . captureSession ? . isRunning = = true {
logInfo ( " ✅ 扫描会话已成功重新启动 " , className : " ScannerViewModel " )
// 检 查 会 话 状 态
} else {
DispatchQueue . main . asyncAfter ( deadline : . now ( ) + 0.2 ) {
logWarning ( " ⚠️ 扫描会话启动失败 " , className : " ScannerViewModel " )
if self . captureSession ? . isRunning = = true {
logInfo ( " ✅ 扫描会话已成功重新启动 " , className : " ScannerViewModel " )
} else {
logWarning ( " ⚠️ 扫描会话启动失败,尝试重新启动 " , className : " ScannerViewModel " )
// 如 果 启 动 失 败 , 再 次 尝 试
DispatchQueue . global ( qos : . userInitiated ) . async {
self . captureSession ? . startRunning ( )
DispatchQueue . main . async {
if self . captureSession ? . isRunning = = true {
logInfo ( " ✅ 扫描会话第二次尝试启动成功 " , className : " ScannerViewModel " )
} else {
logError ( " ❌ 扫描会话启动失败 " , className : " ScannerViewModel " )
}
}
}
}
}
}
}
}
}
}
@ -245,8 +285,17 @@ class ScannerViewModel: NSObject, ObservableObject, AVCaptureMetadataOutputObjec
didOutput metadataObjects : [ AVMetadataObject ] ,
didOutput metadataObjects : [ AVMetadataObject ] ,
from connection : AVCaptureConnection ) {
from connection : AVCaptureConnection ) {
// 防 止 重 复 处 理 检 测 结 果
guard ! isProcessingDetection else {
logInfo ( " ⚠️ 正在处理检测结果,忽略新的检测 " , className : " ScannerViewModel " )
return
}
logInfo ( " metadataOutput 被调用,检测到 \( metadataObjects . count ) 个对象 " , className : " ScannerViewModel " )
logInfo ( " metadataOutput 被调用,检测到 \( metadataObjects . count ) 个对象 " , className : " ScannerViewModel " )
// 设 置 处 理 标 记
isProcessingDetection = true
// 震 动 反 馈
// 震 动 反 馈
AudioServicesPlaySystemSound ( SystemSoundID ( kSystemSoundID_Vibrate ) )
AudioServicesPlaySystemSound ( SystemSoundID ( kSystemSoundID_Vibrate ) )