Swift中使用AVFoundation无法扫描二维码问题求助
AVFoundation二维码扫描无法识别问题排查与修正
核心问题
你的代码未配置QR码的识别元数据类型,这是真机能打开相机但无法扫描二维码的直接原因。
分步修正方案
1. 添加QR码识别类型
在设置metadataOutput.metadataObjectTypes时,加入AVMetadataObject.ObjectType.qr:
metadataOutput.metadataObjectTypes = [ AVMetadataObject.ObjectType.qr, AVMetadataObject.ObjectType.ean8, AVMetadataObject.ObjectType.ean13, AVMetadataObject.ObjectType.pdf417 ]
如果仅需扫描二维码,可只保留.qr类型。
2. 补充相机权限申请
iOS 10及以后必须申请相机权限才能访问摄像头,你当前代码缺少权限处理逻辑:
- 首先在
Info.plist中添加NSCameraUsageDescription键,填写权限说明(例如"需要访问相机以扫描二维码") - 然后在代码中先检查权限,再初始化捕获会话:
override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .black // 先请求相机权限 AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in DispatchQueue.main.async { guard granted else { // 权限被拒绝,引导用户开启 let ac = UIAlertController(title: "权限不足", message: "请在设置中开启相机权限", preferredStyle: .alert) ac.addAction(UIAlertAction(title: "去设置", style: .default) { _ in UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!) }) ac.addAction(UIAlertAction(title: "取消", style: .cancel)) self?.present(ac, animated: true) return } // 权限通过,初始化扫描会话 self?.setupCaptureSession() } } } // 抽离原viewDidLoad中的初始化逻辑 private func setupCaptureSession() { captureSession = AVCaptureSession() guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else { print("当前设备无相机(模拟器)") self.failed() return } let videoInput: AVCaptureDeviceInput do { videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice) } catch { print("相机输入初始化失败:\(error.localizedDescription)") self.failed() return } guard captureSession.canAddInput(videoInput) else { self.failed() return } captureSession.addInput(videoInput) let metadataOutput = AVCaptureMetadataOutput() guard captureSession.canAddOutput(metadataOutput) else { self.failed() return } captureSession.addOutput(metadataOutput) // 用后台队列处理识别逻辑,避免卡顿主线程 let metadataQueue = DispatchQueue(label: "com.yourapp.qrscanner.metadata") metadataOutput.setMetadataObjectsDelegate(self, queue: metadataQueue) metadataOutput.metadataObjectTypes = [.qr, .ean8, .ean13, .pdf417] previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) previewLayer.frame = view.layer.bounds previewLayer.videoGravity = .resizeAspectFill view.layer.addSublayer(previewLayer) captureSession.startRunning() }
3. 优化识别回调的线程安全
如果使用后台队列处理元数据,回调中操作UI必须切回主线程:
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) { captureSession.stopRunning() guard let metadataObject = metadataObjects.first as? AVMetadataMachineReadableCodeObject, let codeValue = metadataObject.stringValue else { return } DispatchQueue.main.async { AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate)) self.found(code: codeValue) self.dismiss(animated: true) } }
4. 其他细节优化
- 避免强制解包:将
readableObject.stringValue!改为可选绑定,防止空值崩溃 - 统一模拟器处理:原代码中无相机分支仅打印提示,建议调用
failed()方法统一弹出提示 - 线程安全:
viewWillAppear中启动会话时,确保UI操作在主线程执行
内容的提问来源于stack exchange,提问作者iOS work
相关产品推荐
相关产品推荐

