You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 17:09:56