(八)深入了解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)?請求權限,并根據權限結果決定是否繼續初始化采集相關流程。

到這里,我們已經完成了拍照功能的基本搭建,感謝大家的閱讀。

?

?

?

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/77370.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/77370.shtml
英文地址,請注明出處:http://en.pswp.cn/web/77370.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

git tag使用場景和實踐

背景 每次上線一個迭代,為了區分本次代碼的分支是哪個迭代的commit,可以給分支打上tag,這樣利于追蹤分支所屬迭代,如果devops沒有自動給分支打tag,需要自己來打 操作 1.查看當前tag git tag2.給分支打tag git tag…

從零開始掌握Linux數據流:管道與重定向完全指南

全文目錄 1 知識背景與核心概念1.1 操作系統的輸入輸出模型1.2 Shell 的中間人角色 2 重定向技術深度解析2.1 輸出重定向2.1.1 覆蓋寫2.1.2 追加寫2.1.3 錯誤重定向2.1.4 同時重定向 stdout 和 stderr 2.2 輸入重定向2.2.1 文件作為輸入源2.2.2 Here Document(多行輸…

aws(學習筆記第三十九課) iot-core

文章目錄 aws(學習筆記第三十九課) iotcore(Internet Of Thing)學習內容:1. 整體架構1.1 代碼鏈接1.2 整體架構(概要)1.3 整體架構(詳細 )2. 代碼解析2.1 創建`IOT thing`2.2 創建`AWS IOT certificate`證書2.2.1 創建`lambda`需要的`role`2.2.2 創建`lambda`2.2.3 `lambd…

國家新政鼓勵游戲出海,全球化安全威脅如何解

本文作者:騰訊宙斯盾DDoS防護團隊 01 政策紅利釋放:游戲出海升級為“國家戰略工程” 01 4月21日,國務院新聞辦公室發布《加快推進服務業擴大開放綜合試點工作方案》,釋放了一個信號:首次將“游戲出海”列為戰略級工程&…

MobX 在 React 中的使用:狀態管理的新選擇

🤍 前端開發工程師、技術日更博主、已過CET6 🍨 阿珊和她的貓_CSDN博客專家、23年度博客之星前端領域TOP1 🕠 牛客高級專題作者、打造專欄《前端面試必備》 、《2024面試高頻手撕題》、《前端求職突破計劃》 🍚 藍橋云課簽約作者、…

Idea 配置 Git

1、下載Git 下載地址: Git - Downloading Package 2、win 打開 git bash ,配置郵箱和用戶名 //配置郵箱 git config --global user.email "710419844qq.com" //配置全局用戶名 git config --global user.name "smelodys" 3、ide…

Vue3 + OpenLayers 開發教程 (四) 樣式配置與性能優化

1. 地圖樣式基礎概念 1.1 什么是地圖樣式? 地圖樣式是決定地圖要素(點、線、面)如何顯示的重要配置。在 OpenLayers 中,樣式主要包含以下幾個核心組件: Fill(填充):控制面狀要素的…

【Nacos-安全與限流機制健全06 】

文章目錄 Nacos安全機制介紹Nacos代碼實現Nacos限流機制Nacos限流的代碼實現 Nacos安全機制介紹 一、Nacos安全控制機制 Nacos 提供了多種安全控制機制,以保證服務和配置的訪問安全: 身份驗證 (Authentication) Nacos 支持用戶身份驗證來防止未授權的訪…

自建開源遠程協助服務RustDesk —— 筑夢之路

開源項目 # 服務端https://github.com/rustdesk/rustdesk-server.git# 客戶端https://github.com/rustdesk/rustdesk.git 搭建服務端 需要使用的端口、協議 hbbs - RustDesk ID 注冊服務器 hbbr - RustDesk 中繼服務器默認情況下,hbbs 監聽 21115(tcp) , 21…

Jmeter中同步定時器使用注意點

1.設置數量不可大于總線程數量,不然會一直等待 2.設置數量必須與總線程數量成整數倍數,不然還是要一直等。 3.當配置的數量小于線程數時,最好把循環打開,避免最后一次未準備好的線程數量達不到并發數。

作為高速通道光纖傳輸模式怎么理解以及到底有哪些?

光纖的傳輸模式主要取決于光纖的結構(如纖芯直徑和折射率分布),不同模式對應光波在光纖中傳播的不同路徑和電磁場分布。以下是光纖傳輸模式的主要分類及特點: 1. 單模光纖(Single-Mode Fiber, SMF) 核心特點: 纖芯直徑極小(通常為 8-10微米),僅允許光以單一模式(…

小程序Npm package entry file not found?

修改依賴包的入口文件 看是不是cjs,小程序不支持cjs

Android HAL HIDL

1 Android HAL HIDL 1.1 Android中查看有哪些HIDL HAL HIDL是Treble Interface的一部分。 adb root adb shell # lshal 1.2 Android打印C調用棧 #include <utils/CallStack.h> 在需要打印的地方加如下的定義。 android::CallStack stack("oem"); logcat | g…

【AI 加持下的 Python 編程實戰 2_11】DIY 拓展:從掃雷小游戲開發再探問題分解與 AI 代碼調試能力(下)

&#xff08;接 上篇&#xff09; 5 復盤與 Copilot 的交互過程 前面兩篇文章分別涵蓋了掃雷游戲的問題分解和代碼實現過程&#xff0c;不知道各位是否會有代碼一氣呵成的錯覺&#xff1f;實際上&#xff0c;為了達到最終效果&#xff08;如下所示&#xff09;&#xff0c;我…

游戲狀態管理:用Pygame實現場景切換與暫停功能

游戲狀態管理:用Pygame實現場景切換與暫停功能 在開發游戲時,管理游戲的不同狀態(如主菜單、游戲進行中、暫停等)是非常重要的。這不僅有助于提升玩家的游戲體驗,還能使代碼結構更加清晰。本文將通過一個簡單的示例,展示如何使用Pygame庫來實現游戲中的場景切換和暫停功…

Java后端開發day36--源碼解析:HashMap

&#xff08;以下內容均來自上述課程&#xff09; 1. HashMap&#xff08;一&#xff09; 底層&#xff1a;數組鏈表紅黑樹 1.1 前提準備 查看源碼&#xff1a;選中HashMap–ctrlB 小細節&#xff1a;快捷鍵ctrlf12–跳出目錄結構 藍色圓圈&#xff1a;class 證明是類名粉…

RT-Thread學習筆記(四)

RT-Thread學習筆記 線程間同步信號量信號量的使用和管理動態創建信號量靜態創建信號量獲取信號量信號量同步實列互斥量互斥量的使用和管理互斥量動態創建互斥量靜態創建互斥量獲取和釋放互斥量實例事件集事件集的使用和管理動態創建事件集靜態初始化事件集發送和接收事件事件集…

element ui el-col的高度不一致導致換行

問題&#xff1a;ell-col的高度不一致導致換行&#xff0c;刷新后審查el-col的高度一致 我這邊是el-col寫的span超過了24&#xff0c;自行換行&#xff0c;測試發現初次進入里面的高度渲染的不一致&#xff0c;有的是51px有的是51.5px 問題原因分析 Flex布局換行機制 Elemen…

現代化Android開發:Compose提示信息的最佳封裝方案

在 Android 開發中&#xff0c;良好的用戶反饋機制至關重要。Jetpack Compose 提供了現代化的 UI 構建方式&#xff0c;但提示信息(Toast/Snackbar)的管理往往顯得分散。本文將介紹如何優雅地封裝提示信息&#xff0c;提升代碼可維護性。 一、基礎封裝方案 1. 簡單 Snackbar …

【C++語法】類和對象(2)

4.類和對象&#xff08;2&#xff09; 文章目錄 4.類和對象&#xff08;2&#xff09;類的六個默認成員函數(1)構造函數&#xff1a;構造函數特點含有缺省參數的構造函數構造函數特點&#xff08;續&#xff09;注意事項構造函數補充 前面總結了有關對象概念&#xff0c;對比 C…