From c350669bbaf9d826c914f42855bcebc53b3dbf03 Mon Sep 17 00:00:00 2001 From: v504 Date: Mon, 1 Sep 2025 16:15:40 +0800 Subject: [PATCH] Enhance ScannerViewModel to improve camera permission handling and capture session setup. Update logging messages for clarity and add retry logic for starting the scanning session. Ensure immediate scanning initiation after session setup, enhancing user experience and reliability. --- MyQrCode/Views/Scanner/ScannerViewModel.swift | 81 +++++++++++++++++-- 1 file changed, 76 insertions(+), 5 deletions(-) diff --git a/MyQrCode/Views/Scanner/ScannerViewModel.swift b/MyQrCode/Views/Scanner/ScannerViewModel.swift index afa7ddc..e0d2d73 100644 --- a/MyQrCode/Views/Scanner/ScannerViewModel.swift +++ b/MyQrCode/Views/Scanner/ScannerViewModel.swift @@ -27,9 +27,10 @@ class ScannerViewModel: NSObject, ObservableObject, AVCaptureMetadataOutputObjec switch AVCaptureDevice.authorizationStatus(for: .video) { case .authorized: - logInfo("✅ 相机权限已授权", className: "ScannerViewModel") + logInfo("✅ 相机权限已授权,立即设置捕获会话", className: "ScannerViewModel") cameraAuthorizationStatus = .authorized setupCaptureSession() + // setupCaptureSession 现在会自动启动扫描 case .notDetermined: logInfo("❓ 相机权限未确定,请求权限", className: "ScannerViewModel") @@ -51,9 +52,10 @@ class ScannerViewModel: NSObject, ObservableObject, AVCaptureMetadataOutputObjec AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in DispatchQueue.main.async { if granted { - logInfo("✅ 相机权限请求成功", className: "ScannerViewModel") + logInfo("✅ 相机权限请求成功,立即设置捕获会话", className: "ScannerViewModel") self?.cameraAuthorizationStatus = .authorized self?.setupCaptureSession() + // setupCaptureSession 现在会自动启动扫描 } else { logWarning("❌ 相机权限请求被拒绝", className: "ScannerViewModel") self?.cameraAuthorizationStatus = .denied @@ -78,15 +80,25 @@ class ScannerViewModel: NSObject, ObservableObject, AVCaptureMetadataOutputObjec func refreshCameraPermission() { logInfo("🔍 重新检查相机权限状态", className: "ScannerViewModel") + + // 先停止当前扫描 + if captureSession?.isRunning == true { + stopScanning() + } + + // 重新检查权限 checkCameraPermission() } // MARK: - 相机设置 private func setupCaptureSession() { + logInfo("🔧 开始设置捕获会话", className: "ScannerViewModel") + captureSession = AVCaptureSession() guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else { + logError("❌ 无法获取视频设备", className: "ScannerViewModel") showAlert = true return } @@ -99,13 +111,16 @@ class ScannerViewModel: NSObject, ObservableObject, AVCaptureMetadataOutputObjec do { videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice) } catch { + logError("❌ 创建视频输入失败: \(error.localizedDescription)", className: "ScannerViewModel") showAlert = true return } if captureSession.canAddInput(videoInput) { captureSession.addInput(videoInput) + logInfo("✅ 成功添加视频输入", className: "ScannerViewModel") } else { + logError("❌ 无法添加视频输入", className: "ScannerViewModel") showAlert = true return } @@ -117,10 +132,19 @@ class ScannerViewModel: NSObject, ObservableObject, AVCaptureMetadataOutputObjec captureSession.addOutput(metadataOutput) metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main) metadataOutput.metadataObjectTypes = [.qr, .ean8, .ean13, .code128, .code39, .upce, .pdf417, .aztec] + logInfo("✅ 成功添加元数据输出", className: "ScannerViewModel") } else { + logError("❌ 无法添加元数据输出", className: "ScannerViewModel") showAlert = true return } + + logInfo("✅ 捕获会话设置完成,准备启动扫描", className: "ScannerViewModel") + + // 设置完成后立即启动扫描 + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in + self?.startScanning() + } } // MARK: - 扫描控制 @@ -128,23 +152,70 @@ class ScannerViewModel: NSObject, ObservableObject, AVCaptureMetadataOutputObjec func startScanning() { logInfo("🔄 开始扫描", className: "ScannerViewModel") + // 检查相机权限 + guard cameraAuthorizationStatus == .authorized else { + logWarning("❌ 相机权限未授权,无法启动扫描", className: "ScannerViewModel") + return + } + + // 检查捕获会话是否已设置 + guard captureSession != nil else { + logWarning("⚠️ 捕获会话未设置,重新设置", className: "ScannerViewModel") + setupCaptureSession() + return + } + // 检查会话是否已经在运行 - if captureSession?.isRunning == true { + if captureSession.isRunning { logInfo("ℹ️ 扫描会话已经在运行", className: "ScannerViewModel") return } + // 检查会话配置是否完整 + if captureSession.inputs.isEmpty || captureSession.outputs.isEmpty { + logWarning("⚠️ 捕获会话配置不完整,重新设置", className: "ScannerViewModel") + setupCaptureSession() + return + } + + logInfo("🚀 启动扫描会话", className: "ScannerViewModel") + DispatchQueue.global(qos: .userInitiated).async { [weak self] in guard let self = self else { return } self.captureSession?.startRunning() // 检查启动状态 - DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { if self.captureSession?.isRunning == true { logInfo("✅ 扫描会话启动成功", className: "ScannerViewModel") } else { - logWarning("⚠️ 扫描会话启动失败", className: "ScannerViewModel") + logWarning("⚠️ 扫描会话启动失败,尝试重试", className: "ScannerViewModel") + // 启动失败时重试一次 + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + self.retryStartScanning() + } + } + } + } + } + + private func retryStartScanning() { + logInfo("🔄 重试启动扫描", className: "ScannerViewModel") + + guard captureSession != nil else { + logError("❌ 捕获会话为空,无法重试", className: "ScannerViewModel") + return + } + + DispatchQueue.global(qos: .userInitiated).async { [weak self] in + self?.captureSession?.startRunning() + + DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { + if self?.captureSession?.isRunning == true { + logInfo("✅ 重试启动扫描成功", className: "ScannerViewModel") + } else { + logError("❌ 重试启动扫描失败", className: "ScannerViewModel") } } }