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

Flutter项目集成AVFoundation扫码无画面问题求助

问题排查与解决方案

你的问题核心是相机预览画面不显示,同时存在AVCaptureSession startRunning在主线程调用的性能警告,以下是针对性的修复步骤:

1. 修复预览层布局问题(核心原因)

在setUpView方法中,直接用_view.layer.bounds设置previewLayer.frame时,_view的布局尚未完成,bounds可能是初始零值或错误尺寸,导致预览层无法正常显示。

解决方案:

添加视图布局监听,在视图尺寸变化时实时更新预览层的frame:

private func setUpView(){
    _view.backgroundColor = UIColor.clear
    
    captureSession = AVCaptureSession()
    // 可选:设置会话预设为高清,提升扫描清晰度
    captureSession.sessionPreset = .high

    guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else { return }
    let videoInput: AVCaptureDeviceInput

    do {
        videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
    } catch {
        print("相机输入初始化失败:\(error)")
        return
    }

    if (captureSession.canAddInput(videoInput)) {
        captureSession.addInput(videoInput)
    } else {
        failed()
        return
    }

    let metadataOutput = AVCaptureMetadataOutput()

    if (captureSession.canAddOutput(metadataOutput)) {
        captureSession.addOutput(metadataOutput)
        // 改用后台队列处理元数据,避免阻塞主线程
        let metadataQueue = DispatchQueue(label: "com.ceal.qr.metadata")
        metadataOutput.setMetadataObjectsDelegate(self, queue: metadataQueue)
        metadataOutput.metadataObjectTypes = [.qr]
    } else {
        failed()
        return
    }

    previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
    previewLayer.videoGravity = .resizeAspectFill
    _view.layer.addSublayer(previewLayer)
    
    // 添加布局监听,实时更新预览层尺寸
    _view.addObserver(self, forKeyPath: "bounds", options: [.new], context: nil)
    
    // 移到后台线程启动会话,解决性能警告
    DispatchQueue.global(qos: .userInitiated).async {
        self.captureSession.startRunning()
    }
}

// 实现监听回调
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if keyPath == "bounds" {
        previewLayer.frame = _view.layer.bounds
    }
}

// 在deinit中移除监听,避免内存泄漏
deinit {
    _view.removeObserver(self, forKeyPath: "bounds")
    captureSession?.stopRunning()
}

2. 修复线程警告问题

AVCaptureSession.startRunning()是耗时操作,必须放在后台线程执行,否则会导致主线程阻塞、UI卡顿。上面的代码已将其移到DispatchQueue.global(qos: .userInitiated)后台队列中。

3. 额外优化点

  • 元数据队列优化:将元数据回调的队列从DispatchQueue.main改为自定义后台队列,避免扫描时的UI卡顿。
  • 会话预设设置:添加captureSession.sessionPreset = .high,确保相机输出足够清晰的画面,提升二维码识别成功率。
  • 错误日志补充:在do-catch块中添加错误打印,方便排查相机初始化失败的具体原因。

4. 验证权限配置

请确认Info.plist中已添加NSCameraUsageDescription键,并填写合理的权限说明文本,否则权限申请会被系统拒绝:

<key>NSCameraUsageDescription</key>
<string>需要访问相机以扫描二维码</string>

完成以上修改后,相机预览画面应该能正常显示,同时线程警告也会消失。


内容的提问来源于stack exchange,提问作者BraveEvidence

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 15:20:49