深入解析 iOS 視頻錄制(一):錄制管理核心MWRecordingController 類的設計與實現
深入解析iOS視頻錄制(二):自定義UI的實現???????
深入解析 iOS 視頻錄制(三):完整錄制流程的實現與整合
引言
在之前的兩篇博客中,我們詳細探討了如何實現 iOS 視頻錄制功能的核心部分。第一篇博客《深入解析 iOS 視頻錄制(一):錄制管理核心MWRecordingController 類的設計與實現》介紹了如何設計并實現一個錄制管理類MWRecordingController,該類負責管理視頻錄制的核心功能,包括啟動會話、設置輸入輸出、切換攝像頭以及控制錄制的開始與停止。第二篇《深入解析 iOS 視頻錄制(二):自定義 UI 的實現》中,我們詳細探討了如何通過自定義 UI 提供更流暢的用戶體驗,包括錄制按鈕、預覽視圖、控制視圖等組件的實現與布局。
本篇博客將以這些基礎為依托,重點講解如何在一個 ViewController?中實現完整的視頻錄制流程。我們將把前兩篇博客中的內容整合,展示如何在單一視圖控制器中實現從視頻預覽到錄制控制、從開始錄制到保存視頻文件的全過程。通過這篇博客,你將了解如何將這些不同模塊融合成一個完整、可交互的視頻錄制應用。
希望通過這篇博客,你能深入理解如何實現一個簡單而強大的視頻錄制功能,并為未來的應用開發打下堅實的基礎。
準備工作
在開始實現視頻錄制功能之前,我們需要做好一些必要的準備工作。確保項目配置正確,依賴庫安裝完畢,以及設備權限已正確設置,才能順利進行開發和測試。
項目設置與依賴
首先,確保你的 Xcode 項目已經設置好并配置了正確的依賴項。在本項目中,我們主要依賴 AVFoundation 框架來實現視頻錄制功能。
import AVFoundation
配置 Info.plist 權限請求
iOS 中,應用訪問設備的硬件(如攝像頭和麥克風)時需要請求響應的權限。為了確保應用能順利進行示例錄制,我們需要在 Info.plist 文件中添加以下權限描述:
<key>NSCameraUsageDescription</key>
<string>需要訪問攝像頭來進行視頻錄制</string>
<key>NSMicrophoneUsageDescription</key>
<string>需要訪問麥克風來錄制音頻</string>
這些描述會在應用首次請求權限時,向用戶展示權限請求彈窗。確保根據你應用的功能,調整描述內容,提供清晰且易懂的理由,以提升用戶體驗。
設備要求與測試
為了保證視頻錄制功能能夠順利運行,請確保測試設備支持攝像頭和麥克風,在模擬器中一般無法使用真實攝像頭進行錄制,因此必須使用真實設備進行開發和調試。
錄制完整流程代碼實現
在這一部分,我們將會結合代碼一步一步實現錄制視頻的完整流程。
錄制管理與UI組件集成
MWRecordingViewController是本次實現的核心視圖控制,它將 MWRecordingController 作為錄制的核心控制器,并通過自定義的視圖組件(如MWRecordingPreview、MWRecordingControlView、MWRecordingNavgationView)構建了一個完成的視頻錄制界面。
MWRecordingController
錄制控制器(MWRecordingController):負責啟動會話、開始和停止錄制、切換攝像頭。
/// 錄制控制器private let recordingController = MWRecordingController()
public override func viewDidLoad() {super.viewDidLoad()setupRecording()addPreviewView()addNavgationView()addControlView()}private func setupRecording() {recordingController.recordingSource = self.recordingSource// 設置recordingController.setupSession()// 開始會話recordingController.startSession()recordingController.delegate = self}
MWRecordingPreview
預覽視圖(MWRecordingPreview):負責展示視頻錄制過程中的實時畫面。
/// 預覽視圖private let previewView = MWRecordingPreview()
MWRecordingControlView
控制器視圖(MWRecordingControlView):提供開始/暫停錄制、重新錄制、完成錄制等按鈕。
/// 底部控制視圖private let controlView = MWRecordingControlView()
// 控制視圖private func addControlView() {self.view.addSubview(controlView)controlView.snp.makeConstraints { make inmake.leading.trailing.equalToSuperview()make.height.equalTo(103.0)make.bottom.equalToSuperview().offset(-MW_BOTTOM_SAFE_HEIGHT - 14.0)}// 開始/暫停 錄制controlView.recordButtonClickBlock = { [weak self] inguard let self = self else { return }....}// 重新錄制controlView.reRecordButtonClickBlock = { [weak self] inguard let self = self else { return }.....}// 完成controlView.finishButtonClickBlock = { [weak self] inguard let self = self else { return }...}}
MWRecordingNavgationView
導航欄(MWRecordingNavgationView):提供切換攝像頭和返回的按鈕。
/// 導航欄private let navgationView = MWRecordingNavgationView()
// 導航private func addNavgationView() {self.view.addSubview(navgationView)navgationView.snp.makeConstraints { make inmake.top.equalToSuperview().offset(MW_TOP_SAFE_HEIGHT)make.leading.trailing.equalToSuperview()make.height.equalTo(56.0)}// 返回navgationView.backButtonClickBlock = { [weak self] inguard let self = self else { return }self.exit()}// 切換攝像頭navgationView.switchCameraButtonClickBlock = { [weak self] inguard let self = self else { return }self.recordingController.switchCamera()}}
實現錄制的核心邏輯
在這一部分將會重點講解如何在MWRecordingViewController中實現視頻錄制的核心功能,就是UI組件與用操作的交互,以及UI組件與錄制核心類MWRecordingController的交互。
在創建MWRecordingController時,我們已經設置并啟動了會話,那么第一步我們來實現控制視圖的開始/暫停錄制的功能,在MWRecordingControlView中我們創建了多個閉包。
錄制/暫停
其中recordButtonClickBlock負責回調中間錄制/暫停按鈕的事件。
// 開始/暫停 錄制controlView.recordButtonClickBlock = { [weak self] inguard let self = self else { return }if self.recordingState == .normal {// 正常狀態 開始錄制self.recordingState = .recordingself.recordingController.startRecording()} else if self.recordingState == .recording {// 錄制狀態 暫停self.recordingState = .finishself.recordingController.stopRecording()} else if self.recordingState == .finish {// 完成狀態 重新錄制self.recordingState = .recordingself.recordingController.startRecording()}if self.recordingState == .recording {self.currentDuration = 0self.startTimer()} else {self.stopTimer()}// 隱藏/顯示 切換按鈕self.navgationView.isHiddenSwitchCameraButton = self.recordingState != .normalself.controlView.setRecordingState(state: self.recordingState)}
該按鈕的點擊事件會根據當前的狀態來執行不同的操作。
- normal:當狀態為normal,調用MWRecordingController的開始錄制startRecording()方法,并修改狀態為recording。
- recording:當狀態為recording,調用MWRecordingController的結束錄制stopRecording()方法,并修改狀態為finish。
- finish:當狀態為finish時,直接重新錄制,調用開始錄制方法。
- 切換狀態后,如果狀態為recording,重新開啟定時器,定時讀取錄制時長,否則停止定時器。
- 根據切換后的狀態,更新MWRecordingControlView以及MWRecordingNavgationView視圖的狀態。
重新錄制
點擊重新錄制按鈕,直接切換狀態為錄制狀態,并執行開始錄制,啟動定時器開始讀取錄制時長。
// 重新錄制controlView.reRecordButtonClickBlock = { [weak self] inguard let self = self else { return }self.stopPlay()self.recordingState = .recordingself.controlView.setRecordingState(state: self.recordingState)self.recordingController.startRecording()self.startTimer()}
完成事件
點擊完成事件,首先判斷錄制時長是否符合要求,然后將錄制視頻的URL傳遞回調用的視圖控制器,并隱藏當前錄制的視圖控制器。
// 完成controlView.finishButtonClickBlock = { [weak self] inguard let self = self else { return }// 判斷時長if self.currentDuration < self.minDuration {MWToast.showToast("Video at least \(self.minDuration) seconds")return}self.recordingCompletionHandler?(self.coverImage,self.videoURL)self.exit()}
錄制的代理
在MWRecordingController中我們定義了兩個代理方法,用于回調錄制的錯誤信息以及錄制完成的視頻地址。
//MARK: MWRecordingControllerDelegate/// 錄制報錯func recordingController(_ controller: MWRecordingController, didFailWithError error: any Error) {let image = UIImage(named: "login_toast_left_icon")MWToast.showTopToast(error.localizedDescription, icon: image)}/// 錄制完成func recordingController(_ controller: MWRecordingController, didFinishRecordingTo outputFileURL: URL) {videoURL = outputFileURLlet image = generateCoverImage()coverImage = imageif currentDuration < minDuration {MWToast.showToast("Video at least \(minDuration) seconds")return}}
- 錯誤信息直接顯示Toast。
- 錄制完成后,根據視頻地址讀取視頻封面。
計時器
開啟和關閉計時器,在定時事件中更新錄制時長,如果達到錄制的最大時長自動結束錄制。
/// 開啟定時器private func startTimer() {if timer != nil {return}MWLogHelper.debug("開啟定時器", context: "MWRecordingViewController")timer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true)}/// 結束定時器private func stopTimer() {MWLogHelper.debug("結束定時器", context: "MWRecordingViewController")timer?.invalidate()timer = nil}/// 定時器事件@objc private func timerAction() {// 讀取錄制時長let duration = recordingController.durationcurrentDuration = CMTimeGetSeconds(duration)if currentDuration >= maxDuration {// 結束錄制recordingController.stopRecording()recordingState = .finishcontrolView.setRecordingState(state: recordingState)stopTimer()} else {controlView.updateTime(time: currentDuration)}}
結語
在本篇博客中,我們詳細介紹了如何在 ViewController?中實現完整的 iOS 視頻錄制功能。通過整合之前討論的錄制管理核心 MWRecordingController?和自定義的 UI 組件,我們構建了一個可交互的視頻錄制界面,涵蓋了錄制的各個方面:從視頻預覽、錄制控制到視頻保存與封面生成,提供了一個完整且流暢的用戶體驗。
通過這次實現,大家可以了解到如何使用 AVFoundation?框架來處理視頻錄制,同時也掌握了如何結合自定義 UI 和交互設計,提升應用的易用性與功能性。
在未來,視頻錄制功能的擴展性非常強,可以根據需求加入更多的特性,如視頻特效、實時濾鏡、錄制過程中的實時預覽調整等。隨著技術的不斷發展,如何優化視頻錄制的性能、降低資源消耗、提高錄制質量等,仍然是值得我們不斷探索的方向。
希望本系列博客能夠幫助你更好地理解 iOS 視頻錄制的實現方式,也期待你能將這些知識應用到自己的項目中,打造出更豐富、更有趣的用戶體驗!