视图控制器中相机预览无法即时显示的问题排查
相机预览初始不显示的问题排查与修复
问题描述
在真实iPhone设备上,视图控制器加载完成后调用相机实时预览功能,初始状态下预览画面无法显示;但打开系统相机应用切换前后置摄像头后返回当前应用,预览就能正常工作。
代码中的核心问题
你的代码存在以下几个关键问题,导致初始预览失败:
- 未请求相机权限:系统相机首次启动时会触发权限弹窗,返回后权限已获取,所以预览恢复正常;但你的应用初始未申请权限,相机硬件无法被激活。
- 捕获会话生命周期错误:
captureSession是局部变量,showLivePreview方法执行完毕后会被ARC释放,导致预览层失去会话关联。 - 会话配置顺序混乱:添加输入、输出的顺序不符合AVFoundation规范,且重复添加不必要的输出组件。
- 预览层布局时机错误:
viewDidLoad阶段view.frame尚未最终确定,直接设置会导致预览层位置或尺寸异常。 - 设备选择无容错:直接取
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
相关产品推荐
相关产品推荐

