当前位置: 首页 > news >正文

(八)深入了解AVFoundation-采集:拍照功能的实现

引言

在上一篇文章中,我们初步完成了使用 AVFoundation 采集视频数据的流程,掌握了 AVCaptureSession 的搭建与视频流的预览显示。

本篇将继续深入 AVFoundation,聚焦于静态图片采集的实现。通过 AVCapturePhotoOutput,我们可以快速搭建起拍照功能,并获取高质量的照片数据。

本文将详细介绍拍照的基本流程,包括如何创建输出、配置拍照参数、处理拍照回调,以及如何将拍摄到的照片保存到系统相册。结合实际示例,带你完成一套完整、稳定的拍照功能搭建。

在早期的 iOS 开发中,我们常常使用 AVCaptureStillImageOutput 来实现拍照功能。但从 iOS 10 开始,Apple 推荐使用更强大、功能更全面的 AVCapturePhotoOutput 进行静态图片采集。AVCapturePhotoOutput 不仅统一了拍照接口,还支持 HEIF 格式、Live Photo、深度数据、RAW 拍摄等高级特性,能够更好地满足高质量拍摄的需求。

接下来,我们将基于 AVCapturePhotoOutput,搭建一个基础的拍照流程。

搭建拍照功能

为了更好地管理拍照流程,我们将功能封装到一个自定义的PHCaptureController类中,负责完成会话的搭建、控制与拍照等操作。

整个拍照流程主要包括以下几个步骤:

  1. 配置 AVCaptureSession。
  2. 添加摄像头输入。
  3. 添加照片输出。
  4. 启动与停止会话。
  5. 处理拍照请求与回调。

下面我们来逐一实现。

1. 类的基本结构

我们先来创建一个PHCaptureController类,大致结构如下:

//
//  PHCaptureController.swift
//  PHCaptureExample
//
//  Created by Louis on 2025/4/23.
// 负责会话控制及拍照操作import UIKit
import AVFoundationclass PHCaptureController:NSObject {/// 会话private let session = AVCaptureSession()/// 输出private let photoOutput = AVCapturePhotoOutput()/// 输入private var captureDeviceInput: AVCaptureDeviceInput?/// 队列private let sessionQueue = DispatchQueue(label: "com.example.captureSession")/// 代理weak var delegate: PHCaptureProtocol?init() {}/// 配置会话func setupConfigureSession() { }/// 启动会话func startSession() { }/// 停止会话func stopSession() { }/// 拍照func takePhoto() { }}

可以看到我们在类的内部维护了:

  1. session:采集会话。
  2. photoOutput:用于拍照的会话输出。
  3. captureDeviceInput:当前使用的会话输入。
  4. sessionQueue:串行队列,保证采集相关操作的线程安全。
  5. delegate:代理,负责错误和结果的回调。

PHCaptureProtocol 实现如下:

//
//  PHCaptureProtocol.swift
//  PHCaptureExample
//
//  Created by Louis on 2025/4/23.
//import Foundation
import UIKitprotocol PHCaptureProtocol:NSObjectProtocol {/// 发生错误func captureError(_ error: Error)/// 拍照回调func capturePhoto(_ image: UIImage)}

包含了:

  1. captureError:发生错误的回调方法。
  2. capturePhoto:拍照结果的回调方法。

2. 配置采集会话

我们在 setupConfigureSession 方法中需要完成三个操作:

  1. 设置会话预设。
  2. 添加摄像头输入。
  3. 添加照片输出。

并且保证这些操作需要在会话 beginConfiguration 与 commitConfiguration 方法之间执行。

    /// 配置会话func setupConfigureSession() {session.beginConfiguration()// 1.设置会话预设setupSessionPreset()// 2.设置会话输入if !setupSessionInput() {delegate?.captureError(NSError(domain: "PHCaptureController", code: 1001, userInfo: [NSLocalizedDescriptionKey: "Failed to add input"]))return}// 3.设置会话输出if !setupSessionOutput() {delegate?.captureError(NSError(domain: "PHCaptureController", code: 1002, userInfo: [NSLocalizedDescriptionKey: "Failed to add output"]))return}session.commitConfiguration()}

2.1 设置会话预设

就是设置分辨率,比如高质量照片。

    /// 设置会话话预设private func setupSessionPreset() {session.sessionPreset = .photo}

2.2 设置会话输入

添加摄像头设备,并包装一层 AVCaptureDeviceInput 添加到会话中。

    /// 设置会话输入private func setupSessionInput() -> Bool {guard let device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video,position: .back) else { return false }do {captureDeviceInput = try AVCaptureDeviceInput(device: device)if session.canAddInput(captureDeviceInput!) {session.addInput(captureDeviceInput!)return true} else {return false}} catch {delegate?.captureError(error)return false}}
  1. 首先要确保获取到了摄像头设备。
  2. 检测输入是否可以被添加到会话。
  3. 如果出现错误则直接回调。

2.3 设置会话输出

将图片输出添加到会话。

    /// 设置会话输出private func setupSessionOutput() -> Bool {if session.canAddOutput(photoOutput) {session.addOutput(photoOutput)return true} else {return false}}

添加输出时也要检测是否可以被添加到会话。

3. 会话的启动与停止

我们使用单独的串行队列来管理会话的启动与停止,避免出现线程问题。

    /// 启动会话func startSession() {sessionQueue.async {if !self.session.isRunning {self.session.startRunning()}}}/// 停止会话func stopSession() {sessionQueue.async {if self.session.isRunning {self.session.stopRunning()}}}

4. 拍照

在拍照的方法里设置拍照参数和代理,并实现 AVCapturePhotoCaptureDelegate 的代理方法。

    /// 拍照func takePhoto() {let settings = AVCapturePhotoSettings()settings.flashMode = .autosettings.isHighResolutionPhotoEnabled = truesessionQueue.async {self.photoOutput.capturePhoto(with: settings, delegate: self)}}//MARK: - AVCapturePhotoCaptureDelegatefunc photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: (any Error)?) {if let error = error {delegate?.captureError(error)return}guard let imageData = photo.fileDataRepresentation() else {delegate?.captureError(NSError(domain: "PHCaptureController", code: 1003, userInfo: [NSLocalizedDescriptionKey: "Failed to get image data"]))return}guard let image = UIImage(data: imageData) else {delegate?.captureError(NSError(domain: "PHCaptureController", code: 1004, userInfo: [NSLocalizedDescriptionKey: "Failed to create image"]))return}delegate?.capturePhoto(image)}

整个拍照的核心功能就已经搭建完成了,接下来我们只需要添加画面预览,就可以调用这个类来实现完整的拍照功能了。

5. 画面预览

为了能够实时显示摄像头画面,我们需要一个专门的预览视图。

在 AVFoundation 中,通常通过 AVCaptureVideoPreviewLayer 来实现摄像头画面渲染,因此我们可以自定义一个简单的 PHPreviewView。

import UIKit
import AVFoundationclass PHPreviewView: UIView {override class var layerClass: AnyClass {return AVCaptureVideoPreviewLayer.self}private var previewLayer: AVCaptureVideoPreviewLayer {return layer as! AVCaptureVideoPreviewLayer}func setSession(_ session: AVCaptureSession) {previewLayer.session = sessionpreviewLayer.videoGravity = .resizeAspectFill}
}

使用拍照功能

接下来我们只需要在视图控制器中,非常简单的接入一下拍照的控制器和预览视图就可以使用拍照功能咯。

import UIKit
import AVFoundationclass ViewController: UIViewController,PHCaptureProtocol {/// 拍照控制器let captureController = PHCaptureController()/// 预览视图let previewView = PHPreviewView()override func viewDidLoad() {super.viewDidLoad()captureController.setupConfigureSession()captureController.delegate = selfcaptureController.startSession()previewView.setSession(captureController.session)view.addSubview(previewView)previewView.frame = view.bounds}//MARK: - PHCaptureProtocolfunc captureError(_ error: any Error) {}func capturePhoto(_ image: UIImage) {}}

结语

在本篇中,我们基于 AVFoundation 框架,搭建了一个基本的拍照功能实现流程:

包括配置 AVCaptureSession、添加 AVCaptureDeviceInput 和 AVCapturePhotoOutput、设置预览视图 PHPreviewView,并通过 AVCapturePhotoCaptureDelegate 拿到照片数据,为后续保存、展示、处理照片打下了基础。

需要特别注意的是:在正式使用相机功能之前,务必进行权限申请和检测。

如果未申请或未获得相机访问权限,直接启动 AVCaptureSession 会导致应用崩溃或黑屏。

通常,我们会在 App 启动或功能入口时,通过 AVCaptureDevice.requestAccess(for: .video) 请求权限,并根据权限结果决定是否继续初始化采集相关流程。

到这里,我们已经完成了拍照功能的基本搭建,感谢大家的阅读。

 

 

 

 

相关文章:

  • PyTorch生成式人工智能实战(2)——PyTorch基础
  • day002
  • SpringAI+DeepSeek大模型应用开发实战视频教程,传统Java项目AI化转型必学课程
  • nfs服务原理、搭建手册、安全配置建议及异常定位手段
  • MCP实战-本地MCP Server+Cursor实践
  • 数据库+Docker+SSH三合一!深度评测HexHub的全栈开发体验
  • 备忘录模式:实现对象状态撤销与恢复的设计模式
  • 常用运行指令
  • [Java]动态代理
  • 5.学习笔记-SpringMVC(P61-P70)
  • 3.4/Q1,GBD数据库最新文章解读
  • 抽象工厂模式:创建产品族的设计模式
  • [C#]反射的实战应用,实际数据模拟
  • 机器人项目管理新风口:如何高效推动智能机器人研发?
  • 第七部分:向量数据库和索引策略
  • 养生之道:在岁月里雕琢健康之美
  • 【刷题系列】LeetCode消失的数字、轮转数组
  • 图论---Bellman-Ford算法
  • Excel处理控件Spire.XLS系列教程:Java设置Excel活动工作表或活动单元格
  • 拼团退款中采用分片处理降低对数据库
  • 体坛联播|卡马文加预计伤缺三个月,阿尔卡拉斯因伤退赛
  • 【社论】上海经济开门红:不偏科、挑大梁
  • 魔都眼·上海车展④|奔驰宝马保时捷……全球豪车扎堆首秀
  • 为什么要读书?——北京地铁春季书单(2025)
  • 陕西一批干部任职公示:西安市未央、雁塔、阎良区委书记拟调整
  • 对话地铁读书人|财务管理孟先生:老婆让我看《三体》