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

如何基于应用与视图全生命周期实现AVCaptureSession的最优处理?

这确实是iOS相机开发里一个容易让人纠结的点,我做过几个相机类应用,踩过不少坑,结合Apple官方文档的隐含逻辑和实际场景的验证,给你梳理下兼顾全生命周期的最优方案:

核心原则:延迟初始化 + 按需恢复 + 及时清理

相机资源属于系统敏感资源,系统会严格管控其使用,同时App生命周期的状态变化(后台/前台、视图显隐)都会影响AVCaptureSession的可用性,所以核心思路是不在视图初始化阶段硬塞所有逻辑,而是跟着视图和App的状态动态调整。

1. 权限检查:前置到viewDidLoad,但不初始化Session

viewDidLoad是最早可以发起相机权限请求的时机,但此时不要急着创建AVCaptureSession——因为权限还没确定,而且视图还没完成布局,预览层的尺寸也不准确。

示例代码:

override func viewDidLoad() {
    super.viewDidLoad()
    // 发起相机权限请求
    AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in
        DispatchQueue.main.async {
            if granted {
                // 权限通过,标记状态,等待后续初始化
                self?.hasCameraPermission = true
            } else {
                // 提示用户去设置开启权限
                self?.showPermissionAlert()
            }
        }
    }
    // 监听权限变化通知(用户可能在设置里改权限)
    NotificationCenter.default.addObserver(self, selector: #selector(cameraPermissionChanged), name: NSNotification.Name.AVCaptureDeviceAuthorizationStatusDidChange, object: nil)
}

2. Session初始化与启动:放在viewDidAppear

选择viewDidAppear而非viewWillAppear的原因:

  • 此时视图已经完全显示,布局完成,预览层的bounds是准确的,不会出现预览画面拉伸/偏移的问题
  • 用户已经看到相机界面,此时如果初始化失败,可以直接在界面上提示(比如“相机初始化失败,请重试”),体验更自然

具体步骤:

  • 检查权限是否通过
  • 创建AVCaptureSession,配置输入(前后置相机)、输出(照片/视频)
  • 添加预览层到视图上
  • 启动Session(注意要在异步队列执行,避免阻塞主线程)

示例代码:

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    guard hasCameraPermission, !isSessionRunning else { return }
    
    DispatchQueue.global(qos: .userInitiated).async { [weak self] in
        guard let self = self else { return }
        let session = AVCaptureSession()
        session.sessionPreset = .photo
        
        // 配置输入(以后置相机为例)
        guard let backCamera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back),
              let input = try? AVCaptureDeviceInput(device: backCamera) else {
            DispatchQueue.main.async {
                self.showCameraErrorAlert()
            }
            return
        }
        
        // 配置输出
        let photoOutput = AVCapturePhotoOutput()
        if session.canAddInput(input), session.canAddOutput(photoOutput) {
            session.addInput(input)
            session.addOutput(photoOutput)
            
            // 配置预览层
            DispatchQueue.main.async {
                let previewLayer = AVCaptureVideoPreviewLayer(session: session)
                previewLayer.frame = self.previewView.bounds
                previewLayer.videoGravity = .resizeAspectFill
                self.previewView.layer.addSublayer(previewLayer)
                self.previewLayer = previewLayer
            }
            
            // 启动Session
            session.startRunning()
            self.captureSession = session
            self.isSessionRunning = true
        }
    }
}

3. 后台/前台切换、视图重显:在viewWillAppear做恢复检查

当App进入后台时,系统会自动暂停AVCaptureSession,甚至在内存紧张时回收相机资源。所以每次视图即将显示时,需要检查Session状态并恢复:

  • 如果Session存在但未运行,尝试重新启动
  • 如果Session已被销毁(比如captureSession为nil),则重新执行初始化逻辑
  • 处理相机切换失败的情况:切换前后置相机时,必须先停止Session,再更换输入,然后重新启动——这个过程如果失败,要捕获错误并提示用户

示例代码:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    guard hasCameraPermission else { return }
    
    if let session = captureSession {
        if !session.isRunning {
            DispatchQueue.global(qos: .userInitiated).async {
                session.startRunning()
                self.isSessionRunning = true
            }
        }
    } else {
        // Session已销毁,重新初始化
        setupCaptureSession()
    }
}

4. 资源清理:在viewWillDisappear及时释放

当视图即将消失时,必须停止Session并清理资源,避免内存泄漏,同时也能让系统及时回收相机资源给其他App使用:

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    if let session = captureSession, session.isRunning {
        session.stopRunning()
        isSessionRunning = false
    }
    previewLayer?.removeFromSuperlayer()
    captureSession = nil
}

5. 全局错误处理:监听Session状态变化

通过AVCaptureSession的代理方法或者KVO监听状态变化,处理初始化失败、启动失败、相机被占用等异常情况:

// 实现AVCaptureSessionDelegate
func session(_ session: AVCaptureSession, didFailWithError error: Error) {
    DispatchQueue.main.async {
        self.showErrorAlert(message: "相机出错:\(error.localizedDescription)")
        // 标记Session失效,下次viewWillAppear时重新初始化
        self.captureSession = nil
        self.isSessionRunning = false
    }
}

总结最优流程

  1. viewDidLoad:发起权限请求,监听权限变化和App生命周期通知
  2. viewDidAppear:权限通过时,初始化Session、配置输入输出、启动Session
  3. viewWillAppear:检查Session状态,按需恢复或重新初始化
  4. viewWillDisappear:停止Session,清理预览层和Session实例
  5. 代理/通知:全局捕获相机错误,提供重试提示

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 17:53:13