使用AVFoundation完成照片拍攝存儲相冊, 開啓關閉閃光燈, 切換攝像頭

在開啓這個旅程以前, 請記住, AVFoundation是一個複雜的工具. 在不少狀況下, 我咱們使用蘋果默認的API(好比:UIImagePickerController)就足夠了. git

在您閱讀以前, 請確保您確實使用過AVFoundationgithub

因爲swift 版本不一樣, 你可能在XCode上面編寫時候部分語法有差別,不過相信廣大小夥伴們都是能夠簡單應對的.😆😆😆swift

 

回話, 設備, 輸入和輸出api

拍攝照片和視頻的核心是CaptureSession(捕獲回話). 在蘋果的介紹中捕獲回話是"an object that manages capture activity and coordinates the flow of data from input devices to capture outputs." 一種管理捕獲活動並協調來自輸入設備的數據流以捕獲輸出的對象。另外, 捕獲設備用於實際的iOS設備上可用的物理音頻和視頻捕獲設備(即攝像頭和麥克風), 若是要使用AVFoundation須要咱們捕獲設備(即獲取攝像頭和麥克風對象).數組

而後經過捕獲輸入, 提供給回話對象, 在將結果保存在輸出中. 通俗說就是同涉嫌頭(輸入對象) 將拍攝的結果提供給會對對象, 在由回話對象將結果顯示屏幕或者其餘輸出對象中安全

而後在將結果保存在捕獲的對象. session

示例圖:框架

 

示例項目異步

若是你想經過本身的手來探索框架, 請您在實例項目上工做, 可是爲了讓咱們專一於討論AVFoundation框架, 我附帶了一個入門項目, 在繼續以前, 請點擊下載並快速查看.async

示例項目是當前項目的基礎, 裏面包含了:

1. Assets.xcassets 包含咱們項目所須要的必要的圖像文件. 聲明:這些圖片來自於互聯網. 若是有涉及侵權, 請聯繫我, 我將第一時間予以刪除

2.裏面有一個Storyboard文件. 此視圖控制器將用於處理咱們的應用程序內全部圖片和視頻的拍攝

3.一個ViewController控制器,用於處理交互

以下圖

 

好的, 咱們開始吧!

在此咱們將設計一個CameraController, 負責完成拍攝照片和視頻錄製有關的工做

請在咱們的項目中聲明一個類 CameraController 類, 繼承NSObject

1 import AVFoundation
2 
3 class CameraController: NSObject { }

 

照片拍攝

首先,咱們將使用後置攝像頭實現照片捕捉功能。這將是咱們的基本功能,咱們將添加切換相機,使用閃光燈,並添加到咱們的照片捕捉功能錄製視頻的能力。因爲配置和啓動捕獲會話是一個相對密集的過程,咱們將解耦它init並建立一個叫作prepare的函數,準備捕獲會話以供使用,並在完成時調用完成處理程序。

1     
2 func prepare(completionHandler: @escaping (Error?) -> Void) { }

這個函數將處理新捕獲會話的建立和配置。請記住,設置捕獲會話由4個步驟組成:

  1. 建立一個捕獲會話。
  2. 獲取和配置必要的捕獲設備。
  3. 使用捕獲設備建立輸入。
  4. 配置照片輸出對象以處理拍攝的圖像。

咱們將使用Swift的嵌套函數以可管理的方式封裝咱們的代碼。首先聲明4個空函數prepare,而後調用它們:

 1 func prepare(completionHandler: @escaping (Error?) -> Void) {
 2     func createCaptureSession() { }
 3     func configureCaptureDevices() throws { }
 4     func configureDeviceInputs() throws { }
 5     func configurePhotoOutput() throws { }
 6     
 7     DispatchQueue(label: "prepare").async {
 8         do {
 9             createCaptureSession()
10             try configureCaptureDevices()
11             try configureDeviceInputs()
12             try configurePhotoOutput()
13         }
14             
15         catch {
16             DispatchQueue.main.async {
17                 completionHandler(error)
18             }
19             
20             return
21         }
22         
23         DispatchQueue.main.async {
24             completionHandler(nil)
25         }
26     }
27 }

在上面的代碼中,咱們建立了樣板函數來執行準備AVCaptureSession照片捕捉的4個關鍵步驟。咱們還設置了一個異步執行的塊,調用這四個函數,必要時捕獲任何錯誤,而後調用完成處理程序。咱們所要作的就是實現這四個功能!咱們開始吧createCaptureSession

 

建立捕獲會話

配置給定以前AVCaptureSession,咱們須要建立它!將如下屬性添加到您的CameraController.swift文件中:

var captureSession: AVCaptureSession?

接下來,將如下內容添加到createCaptureSession嵌套在您的函數的主體中prepare

self.captureSession = AVCaptureSession()

這是簡單的代碼; 它只是建立一個新的AVCaptureSession並將其存儲在captureSession屬性中。

 

如今咱們已經建立了一個AVCaptureSession,咱們須要建立AVCaptureDevice對象來表示實際的iOS設備的攝像頭。繼續並將如下屬性添加到您的CameraController班級。咱們如今要添加frontCamerarearCamera屬性,由於咱們將設置多攝像頭捕獲的基礎知識,並實現稍後更改攝像頭的功能。

1 var frontCamera: AVCaptureDevice?
2 var rearCamera: AVCaptureDevice?

接下來,在裏面聲明一個嵌入的類型CameraController.swift咱們將使用此嵌入式類型來管理建立捕獲會話時可能遇到的各類錯誤:

enum CameraControllerError: Swift.Error {
        case captureSessionAlreadyRunning
        case captureSessionIsMissing
        case inputsAreInvalid
        case invalidOperation
        case noCamerasAvailable
        case unknown
    }

如今談到有趣的部分!讓咱們找到設備上可用的相機。咱們能夠這樣作AVCaptureDeviceDiscoverySession將如下內容添加到configureCaptureDevices

 1 //1
 2 let session = AVCaptureDeviceDiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaTypeVideo, position: .unspecified)
 3 guard let cameras = (session?.devices.flatMap { $0 }), !cameras.isEmpty else { throw CameraControllerError.noCamerasAvailable }
 4  
 5 //2
 6 for camera in cameras {
 7     if camera.position == .front {
 8         self.frontCamera = camera
 9     }
10  
11     if camera.position == .back {
12         self.rearCamera = camera
13  
14         try camera.lockForConfiguration()
15         camera.focusMode = .continuousAutoFocus
16         camera.unlockForConfiguration()
17     }
18 }

這就是咱們剛剛作的:

  1. 這兩行代碼用於AVCaptureDeviceDiscoverySession查找當前設備上可用的全部廣角相機,並將其轉換爲非可選AVCaptureDevice實例的數組若是沒有相機可用,咱們會拋出一個錯誤。
  2. 該循環經過代碼段1中找到的可用攝像頭進行查看,並肯定哪一個是前攝像頭,哪一個是後攝像頭。它還將後置攝像頭配置爲自動對焦,並拋出沿途遇到的任何錯誤。

咱們曾經AVCaptureDeviceDiscoverySession在設備上找到可用的攝像機,並將其配置爲符合咱們的規格。讓咱們將它們鏈接到咱們的捕獲會話。

 

配置設備輸入

如今咱們能夠建立捕獲設備輸入,捕獲設備並將它們鏈接到咱們的捕獲會話。在咱們這樣作以前,添加如下屬性CameraController以確保咱們能夠存儲咱們的輸入:

1 var currentCameraPosition: CameraPosition?
2 var frontCameraInput: AVCaptureDeviceInput?
3 var rearCameraInput: AVCaptureDeviceInput?

咱們的代碼不會在這個狀態下編譯,由於CameraPosition沒有定義。咱們來定義它。將其添加爲如下內嵌類型CameraController

1 public enum CameraPosition {
2     case front
3     case rear
4 }

如今,咱們擁有存儲和管理捕獲設備輸入的全部必要特性。咱們來實現configureDeviceInputs

 1 func configureDeviceInputs() throws {
 2     //3
 3     guard let captureSession = self.captureSession else { throw CameraControllerError.captureSessionIsMissing }
 4  
 5     //4
 6     if let rearCamera = self.rearCamera {
 7         self.rearCameraInput = try AVCaptureDeviceInput(device: rearCamera)
 8  
 9         if captureSession.canAddInput(self.rearCameraInput!) { captureSession.addInput(self.rearCameraInput!) }
10  
11         self.currentCameraPosition = .rear
12     }
13  
14     else if let frontCamera = self.frontCamera {
15         self.frontCameraInput = try AVCaptureDeviceInput(device: frontCamera)
16  
17         if captureSession.canAddInput(self.frontCameraInput!) { captureSession.addInput(self.frontCameraInput!) }
18         else { throw CameraControllerError.inputsAreInvalid }
19  
20         self.currentCameraPosition = .front
21     }
22  
23     else { throw CameraControllerError.noCamerasAvailable }
24 }

如下是咱們所作的:

  1. 這條線只是確保captureSession存在。若是沒有,咱們會拋出一個錯誤。
  2. 這些if聲明負責建立必要的捕捉設備輸入以支持照片捕捉。AVFoundation每次捕捉會話只容許一個基於攝像頭的輸入。因爲後置攝像頭傳統上是默認的,所以咱們嘗試從中建立輸入並將其添加到捕獲會話中。若是失敗了,咱們會回到前置攝像頭。若是失敗了,咱們會拋出一個錯誤。

配置照片輸出

直到這一點,咱們已經添加了全部必要的輸入captureSession如今咱們只須要一種方法捕獲會話中獲取必要的數據幸運的是,咱們有AVCapturePhotoOutput添加一個屬性到CameraController

var photoOutput: AVCapturePhotoOutput?

如今,咱們來實現configurePhotoOutput這個:

 1 func configurePhotoOutput() throws {
 2     guard let captureSession = self.captureSession else { throw CameraControllerError.captureSessionIsMissing }
 3  
 4     self.photoOutput = AVCapturePhotoOutput()
 5     self.photoOutput!.setPreparedPhotoSettingsArray([AVCapturePhotoSettings(format: [AVVideoCodecKey : AVVideoCodecJPEG])], completionHandler: nil)
 6  
 7     if captureSession.canAddOutput(self.photoOutput) { captureSession.addOutput(self.photoOutput) }
 8  
 9     captureSession.startRunning()
10 }

這是一個簡單的實現。它只是配置photoOutput,告訴它使用JPEG文件格式的視頻編解碼器。而後,它增長photoOutputcaptureSession最後,它開始captureSession

咱們差很少完成了!你的CameraController.swift文件應該看起來像這樣:

  1 import AVFoundation
  2  
  3 class CameraController {
  4     var captureSession: AVCaptureSession?
  5  
  6     var currentCameraPosition: CameraPosition?
  7  
  8     var frontCamera: AVCaptureDevice?
  9     var frontCameraInput: AVCaptureDeviceInput?
 10  
 11     var photoOutput: AVCapturePhotoOutput?
 12  
 13     var rearCamera: AVCaptureDevice?
 14     var rearCameraInput: AVCaptureDeviceInput?
 15 }
 16  
 17 extension CameraController {
 18     func prepare(completionHandler: @escaping (Error?) -> Void) {
 19         func createCaptureSession() {
 20             self.captureSession = AVCaptureSession()
 21         }
 22  
 23         func configureCaptureDevices() throws {
 24             let session = AVCaptureDeviceDiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaTypeVideo, position: .unspecified)
 25             guard let cameras = (session?.devices.flatMap { $0 }), !cameras.isEmpty else { throw CameraControllerError.noCamerasAvailable }
 26  
 27             for camera in cameras {
 28                 if camera.position == .front {
 29                     self.frontCamera = camera
 30                 }
 31  
 32                 if camera.position == .back {
 33                     self.rearCamera = camera
 34  
 35                     try camera.lockForConfiguration()
 36                     camera.focusMode = .autoFocus
 37                     camera.unlockForConfiguration()
 38                 }
 39             }
 40         }
 41  
 42         func configureDeviceInputs() throws {
 43             guard let captureSession = self.captureSession else { throw CameraControllerError.captureSessionIsMissing }
 44  
 45             if let rearCamera = self.rearCamera {
 46                 self.rearCameraInput = try AVCaptureDeviceInput(device: rearCamera)
 47  
 48                 if captureSession.canAddInput(self.rearCameraInput!) { captureSession.addInput(self.rearCameraInput!) }
 49  
 50                 self.currentCameraPosition = .rear
 51             }
 52  
 53             else if let frontCamera = self.frontCamera {
 54                 self.frontCameraInput = try AVCaptureDeviceInput(device: frontCamera)
 55  
 56                 if captureSession.canAddInput(self.frontCameraInput!) { captureSession.addInput(self.frontCameraInput!) }
 57                 else { throw CameraControllerError.inputsAreInvalid }
 58  
 59                 self.currentCameraPosition = .front
 60             }
 61  
 62             else { throw CameraControllerError.noCamerasAvailable }
 63         }
 64  
 65         func configurePhotoOutput() throws {
 66             guard let captureSession = self.captureSession else { throw CameraControllerError.captureSessionIsMissing }
 67  
 68             self.photoOutput = AVCapturePhotoOutput()
 69             self.photoOutput!.setPreparedPhotoSettingsArray([AVCapturePhotoSettings(format: [AVVideoCodecKey : AVVideoCodecJPEG])], completionHandler: nil)
 70  
 71             if captureSession.canAddOutput(self.photoOutput) { captureSession.addOutput(self.photoOutput) }
 72             captureSession.startRunning()
 73         }
 74  
 75         DispatchQueue(label: "prepare").async {
 76             do {
 77                 createCaptureSession()
 78                 try configureCaptureDevices()
 79                 try configureDeviceInputs()
 80                 try configurePhotoOutput()
 81             }
 82  
 83             catch {
 84                 DispatchQueue.main.async {
 85                     completionHandler(error)
 86                 }
 87  
 88                 return
 89             }
 90  
 91             DispatchQueue.main.async {
 92                 completionHandler(nil)
 93             }
 94         }
 95     }
 96 }
 97  
 98 extension CameraController {
 99     enum CameraControllerError: Swift.Error {
100         case captureSessionAlreadyRunning
101         case captureSessionIsMissing
102         case inputsAreInvalid
103         case invalidOperation
104         case noCamerasAvailable
105         case unknown
106     }
107  
108     public enum CameraPosition {
109         case front
110         case rear
111     }
112 }

注意:我使用擴展來適當地分割代碼。您能夠根據我的的編碼風格定義,但我認爲這是一個很好的作法,由於它使您的代碼更易於讀寫。

 

顯示預覽

如今咱們已經準備好相機設備了,如今是時候顯示它在屏幕上捕捉的內容了。添加另外一個函數CameraController(在prepare以外,稱之爲displayPreview它應該有如下簽名:

func displayPreview(on view: UIView) throws { }

另外,import UIKit在你的CameraController.swift文件中。咱們將須要它來處理UIView

顧名思義,這個函數將負責建立一個捕獲預覽並在提供的視圖上顯示它。讓咱們添加一個屬性CameraController來支持這個功能:

var previewLayer: AVCaptureVideoPreviewLayer?

該屬性將保存顯示輸出的預覽圖層captureSession咱們來實現這個方法:

 1 func displayPreview(on view: UIView) throws {
 2     guard let captureSession = self.captureSession, captureSession.isRunning else { throw CameraControllerError.captureSessionIsMissing }
 3  
 4     self.previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
 5     self.previewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
 6     self.previewLayer?.connection?.videoOrientation = .portrait
 7  
 8     view.layer.insertSublayer(self.previewLayer!, at: 0)
 9     self.previewLayer?.frame = view.frame
10 }

此功能建立一個AVCaptureVideoPreview使用captureSession,將其設置爲縱向,並將其添加到提供的視圖。

 

接線

如今,讓咱們嘗試將全部這些鏈接到咱們的視圖控制器。頭向上ViewController.swift首先,添加一個屬性ViewController.swift

let cameraController = CameraController()

而後,添加一個嵌套函數viewDidLoad():

 1 func configureCameraController() {
 2     cameraController.prepare {(error) in
 3         if let error = error {
 4             print(error)
 5         }
 6  
 7         try? self.cameraController.displayPreview(on: self.capturePreviewView)
 8     }
 9 }
10  
11 configureCameraController()

這個功能簡單地準備咱們的相機控制器,就像咱們設計的那樣。

不幸的是,咱們還有一步。這是蘋果強制執行的安全要求。你必須爲用戶提供一個理由,解釋爲何你的應用程序須要使用相機。打開Info.plist並插入一行:

Privacy - Camera Usage Description

這個鍵告訴用戶當它要求必要的權限時爲何使用相機。

 

你的ViewController.swift文件如今應該是這樣的:

 1 import UIKit
 2  
 3 class ViewController: UIViewController {
 4     let cameraController = CameraController()
 5  
 6     @IBOutlet fileprivate var captureButton: UIButton!
 7  
 8     ///顯示設備攝像機生成的視頻輸出的預覽
 9     @IBOutlet fileprivate var capturePreviewView: UIView!
10  
11     ///容許用戶將相機置於照片模式下。
12     @IBOutlet fileprivate var photoModeButton: UIButton!
13     @IBOutlet fileprivate var toggleCameraButton: UIButton!
14     @IBOutlet fileprivate var toggleFlashButton: UIButton!
15  
16     //容許用戶將攝像機置於視頻模式中。
17     @IBOutlet fileprivate var videoModeButton: UIButton!
18  
19     override var prefersStatusBarHidden: Bool { return true }
20 }
21  
22 extension ViewController {
23     override func viewDidLoad() {
24         func configureCameraController() {
25             cameraController.prepare {(error) in
26                 if let error = error {
27                     print(error)
28                 }
29  
30                 try? self.cameraController.displayPreview(on: self.capturePreviewView)
31             }
32         }
33  
34         func styleCaptureButton() {
35             captureButton.layer.borderColor = UIColor.black.cgColor
36             captureButton.layer.borderWidth = 2
37  
38             captureButton.layer.cornerRadius = min(captureButton.frame.width, captureButton.frame.height) / 2
39         }
40  
41         styleCaptureButton()
42         configureCameraController()
43     }
44 }

編譯並運行你的項目,當系統提示是否容許使用相機時候點擊容許,而後你應該有一個工做捕捉預覽。若是沒有,請從新檢查您的代碼,若是您須要幫助,請留下評論。

沒錯,今天深圳的天氣仍是挺好的.北方的小夥伴大家那裏天氣好嗎? 😁😁😁 

 

 

切換閃光/切換攝像機

 

如今咱們有一個工做預覽,讓咱們添加更多的功能。大多數相機應用程序容許用戶切換相機並啓用或禁用閃光燈。讓咱們也作這個。咱們這樣作後,咱們將添加捕捉圖像並將其保存到相機膠捲的功能。

首先,咱們將啓用切換閃光燈的功能。將此屬性添加到CameraController:

var flashMode = AVCaptureFlashMode.off

如今,轉到ViewController添加一個@IBAction func切換閃光燈:

 1 @IBAction func toggleFlash(_ sender: UIButton) {
 2     if cameraController.flashMode == .on {
 3         cameraController.flashMode = .off
 4         toggleFlashButton.setImage(#imageLiteral(resourceName: "Flash Off Icon"), for: .normal)
 5     }
 6  
 7     else {
 8         cameraController.flashMode = .on
 9         toggleFlashButton.setImage(#imageLiteral(resourceName: "Flash On Icon"), for: .normal)
10     }
11 }

如今,這就是咱們所要作的。咱們的CameraController類將處理閃光燈時,咱們捕捉圖像。讓咱們繼續轉換攝像機。

在AV基礎上切換攝像頭是一件很是容易的事情。咱們只須要刪除現有攝像頭的捕捉輸入,併爲咱們要切換的攝像頭添加一個新的捕捉輸入。讓咱們添加另外一個功能,咱們的CameraController 切換攝像機:

func switchCameras() throws { }

當咱們切換攝像頭時,咱們要麼切換到前置攝像頭,要麼切換到後置攝像頭。因此,咱們在下面聲明2個嵌套函數switchCameras

1 func switchToFrontCamera() throws { }
2 func switchToRearCamera() throws { }

如今,添加如下內容switchCameras()

 1 //5
 2 guard let currentCameraPosition = currentCameraPosition, let captureSession = self.captureSession, captureSession.isRunning else { throw CameraControllerError.captureSessionIsMissing }
 3  
 4 //6
 5 captureSession.beginConfiguration()
 6  
 7 func switchToFrontCamera() throws { }
 8 func switchToRearCamera() throws { }
 9  
10 //7
11 switch currentCameraPosition {
12 case .front:
13     try switchToRearCamera()
14  
15 case .rear:
16     try switchToFrontCamera()
17 }
18  
19 //8
20 captureSession.commitConfiguration()

這就是咱們剛剛作的:

  1. guard聲明確保在嘗試切換攝像機以前咱們有一個有效的正在運行的捕獲會話。它還驗證是否有一個當前活動的攝像頭。
  2. 這一行告訴捕獲會話開始配置。
  3. 這條switch語句調用switchToRearCamera或者switchToFrontCamera,取決於哪一個攝像頭當前處於活動狀態。
  4. 這條線在配置以後提交或保存咱們的捕獲會話。

咱們如今要作的就是實現switchToFrontCameraswitchToRearCamera

 

 1 func switchToFrontCamera() throws {
 2     guard let inputs = captureSession.inputs as? [AVCaptureInput], let rearCameraInput = self.rearCameraInput, inputs.contains(rearCameraInput),
 3         let frontCamera = self.frontCamera else { throw CameraControllerError.invalidOperation }
 4  
 5     self.frontCameraInput = try AVCaptureDeviceInput(device: frontCamera)
 6  
 7     captureSession.removeInput(rearCameraInput)
 8  
 9     if captureSession.canAddInput(self.frontCameraInput!) {
10         captureSession.addInput(self.frontCameraInput!)
11  
12         self.currentCameraPosition = .front
13     }
14  
15     else { throw CameraControllerError.invalidOperation }
16 }
17  
18 func switchToRearCamera() throws {
19     guard let inputs = captureSession.inputs as? [AVCaptureInput], let frontCameraInput = self.frontCameraInput, inputs.contains(frontCameraInput),
20         let rearCamera = self.rearCamera else { throw CameraControllerError.invalidOperation }
21  
22     self.rearCameraInput = try AVCaptureDeviceInput(device: rearCamera)
23  
24     captureSession.removeInput(frontCameraInput)
25  
26     if captureSession.canAddInput(self.rearCameraInput!) {
27         captureSession.addInput(self.rearCameraInput!)
28  
29         self.currentCameraPosition = .rear
30     }
31  
32     else { throw CameraControllerError.invalidOperation }
33 }

兩個函數都有很是類似的實現。他們首先得到捕獲會話中全部輸入的數組,並確保能夠切換到請求相機。接下來,他們建立必要的輸入設備,刪除舊的,並添加新的。最後,他們設定currentCameraPositionCameraController班級知道變化。簡單!回到ViewController.swift咱們能夠添加一個功能來切換相機:

 1 @IBAction func switchCameras(_ sender: UIButton) {
 2     do {
 3         try cameraController.switchCameras()
 4     }
 5  
 6     catch {
 7         print(error)
 8     }
 9  
10     switch cameraController.currentCameraPosition {
11     case .some(.front):
12         toggleCameraButton.setImage(#imageLiteral(resourceName: "Front Camera Icon"), for: .normal)
13  
14     case .some(.rear):
15         toggleCameraButton.setImage(#imageLiteral(resourceName: "Rear Camera Icon"), for: .normal)
16  
17     case .none:
18         return
19     }
20 }

打開你的故事板,鏈接必要的插座,並構建和運行應用程序。你應該能夠自由切換相機。如今咱們來實現最重要的功能:圖像捕捉!

 

實現圖像捕捉

 

如今咱們能夠實現咱們一直在等待的功能:圖像捕捉在咱們進入以前,讓咱們快速回顧一下迄今爲止所作的一切:

  • 設計了一個可用於輕鬆隱藏AV基金會複雜性的實用工具類。
  • 在這個類中實現了功能,容許咱們建立一個捕捉會話,使用閃光燈,切換攝像頭,並得到工做預覽。
  • 鏈接咱們的課程,UIViewController並創建一個輕量級的相機應用程序。

咱們所要作的只是捕捉圖像!

打開CameraController.swift,讓咱們開始工做。captureImage用這個簽名添加一個函數:

1 func captureImage(completion: (UIImage?, Error?) -> Void) {
2  
3 }

顧名思義,這個功能將會使用咱們製做的相機控制器爲咱們拍攝一張圖像。讓咱們來實現它:

1 func captureImage(completion: @escaping (UIImage?, Error?) -> Void) {
2     guard let captureSession = captureSession, captureSession.isRunning else { completion(nil, CameraControllerError.captureSessionIsMissing); return }
3  
4     let settings = AVCapturePhotoSettings()
5     settings.flashMode = self.flashMode
6  
7     self.photoOutput?.capturePhoto(with: settings, delegate: self)
8     self.photoCaptureCompletionBlock = completion
9 }

這不是一個複雜的實現,但咱們的代碼還不能編譯,由於咱們沒有定義photoCaptureCompletionBlockCameraController不符合AVCapturePhotoCaptureDelegate。首先,咱們添加一個屬性photoCaptureCompletionBlockCameraController

var photoCaptureCompletionBlock: ((UIImage?, Error?) -> Void)?

如今,咱們來擴展CameraController以符合AVCapturePhotoCaptureDelegate:

 1 extension CameraController: AVCapturePhotoCaptureDelegate {
 2     public func capture(_ captureOutput: AVCapturePhotoOutput, didFinishProcessingPhotoSampleBuffer photoSampleBuffer: CMSampleBuffer?, previewPhotoSampleBuffer: CMSampleBuffer?,
 3                         resolvedSettings: AVCaptureResolvedPhotoSettings, bracketSettings: AVCaptureBracketedStillImageSettings?, error: Swift.Error?) {
 4         if let error = error { self.photoCaptureCompletionBlock?(nil, error) }
 5             
 6         else if let buffer = photoSampleBuffer, let data = AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer: buffer, previewPhotoSampleBuffer: nil),
 7             let image = UIImage(data: data) {
 8             
 9             self.photoCaptureCompletionBlock?(image, nil)
10         }
11             
12         else {
13             self.photoCaptureCompletionBlock?(nil, CameraControllerError.unknown)
14         }
15     }
16 }

如今回頭再來ViewController一次。首先,導入Photos框架,由於咱們將使用內置的API來保存照片。

import Photos

而後插入如下函數:

 1 @IBAction func captureImage(_ sender: UIButton) {
 2     cameraController.captureImage {(image, error) in
 3         guard let image = image else {
 4             print(error ?? "Image capture error")
 5             return
 6         }
 7         
 8         try? PHPhotoLibrary.shared().performChangesAndWait {
 9             PHAssetChangeRequest.creationRequestForAsset(from: image)
10         }
11     }
12 }

咱們只需調用captureImage相機控制器方法來拍攝照片,而後使用PHPhotoLibary該類將圖像保存到內置的照片庫中。

最後,鏈接@IBAction func到故事板中的捕獲按鈕,而後Info.plist轉到插入一行:

Privacy - Camera Usage Description

 

這是iOS 10中引入的隱私要求。您必須指定您的應用程序須要訪問照片庫的緣由。

如今創建並運行應用程序來捕捉照片!以後,打開你的照片庫。你應該看到你剛剛拍攝的照片。恭喜,你如今知道如何在您的應用程序中使用AV基金會!祝你好運,並繼續關注本教程的第二部分,咱們將學習如何捕獲視頻。

對於完整的項目,你能夠點擊下載

 

特此聲明:以上全部信息僅僅提供學習使用, 部分功能若是能夠幫助你解決項目中的問題, 我也很開心. 後面我也會將錄製視頻的功能完善上去.

 

 

一個就任於汽車物聯網公司的 iOS 開發者

相關文章
相關標籤/搜索