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

视图控制器中相机预览无法即时显示的问题排查

相机预览初始不显示的问题排查与修复

问题描述

在真实iPhone设备上,视图控制器加载完成后调用相机实时预览功能,初始状态下预览画面无法显示;但打开系统相机应用切换前后置摄像头后返回当前应用,预览就能正常工作。

代码中的核心问题

你的代码存在以下几个关键问题,导致初始预览失败:

  1. 未请求相机权限:系统相机首次启动时会触发权限弹窗,返回后权限已获取,所以预览恢复正常;但你的应用初始未申请权限,相机硬件无法被激活。
  2. 捕获会话生命周期错误:captureSession是局部变量,showLivePreview方法执行完毕后会被ARC释放,导致预览层失去会话关联。
  3. 会话配置顺序混乱:添加输入、输出的顺序不符合AVFoundation规范,且重复添加不必要的输出组件。
  4. 预览层布局时机错误:viewDidLoad阶段view.frame尚未最终确定,直接设置会导致预览层位置或尺寸异常。
  5. 设备选择无容错:直接取devices.first未处理设备为空的情况,可能导致初始化失败。

修复方案

1. 配置相机权限

在Info.plist中添加相机权限描述:

<key>NSCameraUsageDescription</key>
<string>需要访问相机拍摄个人资料照片</string>

2. 修正Swift工具类代码

将会话和预览层改为类属性,确保生命周期与视图控制器一致,并调整配置流程:

class Utilities: NSObject {
    static let shared = Utilities()
    
    // 改为类属性,避免被释放
    private var captureSession: AVCaptureSession?
    private var previewLayer: AVCaptureVideoPreviewLayer?
    
    @objc func showLivePreview(view: UIView) {
        // 先检查相机权限
        switch AVCaptureDevice.authorizationStatus(for: .video) {
        case .authorized:
            setupCameraSession(in: view)
        case .notDetermined:
            AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in
                guard granted, let self = self else { return }
                DispatchQueue.main.async {
                    self.setupCameraSession(in: view)
                }
            }
        default:
            // 权限被拒绝,提示用户去设置开启
            print("相机权限被拒绝,请在设置中开启")
            return
        }
    }
    
    private func setupCameraSession(in view: UIView) {
        let session = AVCaptureSession()
        session.sessionPreset = .photo
        
        // 查找前置摄像头
        let discoverySession = AVCaptureDevice.DiscoverySession(
            deviceTypes: [.builtInWideAngleCamera],
            mediaType: .video,
            position: .front
        )
        
        guard let cameraDevice = discoverySession.devices.first else {
            print("未找到前置摄像头")
            return
        }
        
        do {
            // 创建输入并添加到会话
            let input = try AVCaptureDeviceInput(device: cameraDevice)
            guard session.canAddInput(input) else {
                print("无法添加相机输入")
                return
            }
            session.addInput(input)
            
            // 添加照片输出(如果需要拍摄照片)
            let photoOutput = AVCapturePhotoOutput()
            if session.canAddOutput(photoOutput) {
                session.addOutput(photoOutput)
            }
            
            // 创建并配置预览层
            let previewLayer = AVCaptureVideoPreviewLayer(session: session)
            previewLayer.videoGravity = .resizeAspectFill
            previewLayer.connection?.videoOrientation = .portrait
            previewLayer.frame = view.bounds // 使用bounds确保尺寸正确
            view.layer.addSublayer(previewLayer)
            
            // 设置视图圆角
            view.layer.cornerRadius = view.bounds.width / 2
            view.layer.masksToBounds = true
            
            // 保存会话和预览层引用
            self.captureSession = session
            self.previewLayer = previewLayer
            
            // 在后台线程启动会话
            DispatchQueue.global().async {
                session.startRunning()
            }
            
        } catch {
            print("相机初始化失败:\(error.localizedDescription)")
            return
        }
    }
}

3. 调整Objective-C调用时机

将调用从viewDidLoad移到viewDidAppear,确保视图布局完成后再初始化预览:

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    
    [[Utilities shared] showLivePreviewWithView:self.view];
}

额外说明

  • 若需要支持摄像头切换,需单独保存前后置设备引用,并在切换时重新配置会话(停止会话、移除旧输入、添加新输入、重启会话)。
  • 预览层的frame建议在viewDidLayoutSubviews中更新,避免设备旋转或视图尺寸变化导致预览错位。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 01:03:11