在开启这个旅程之前, 请记住, AVFoundation是一个复杂的工具. 在很多情况下, 我我们使用苹果默认的API(比如:UIImagePickerController)就足够了.
在您阅读之前, 请确保您确实使用过AVFoundation
由于swift 版本不同, 你可能在XCode上面编写时候部分语法有差异,不过相信广大小伙伴们都是可以简单应对的.
回话, 设备, 输入和输出
拍摄照片和视频的核心是CaptureSession(捕获回话). 在苹果的介绍中捕获回话是"an object that manages capture activity and coordinates the flow of data from input devices to capture outputs." 一种管理捕获活动并协调来自输入设备的数据流以捕获输出的对象。另外, 捕获设备用于实际的iOS设备上可用的物理音频和视频捕获设备(即摄像头和麦克风), 如果要使用AVFoundation需要我们捕获设备(即获取摄像头和麦克风对象).
然后通过捕获输入, 提供给回话对象, 在将结果保存在输出中. 通俗说就是同涉嫌头(输入对象) 将拍摄的结果提供给会对对象, 在由回话对象将结果显示屏幕或者其他输出对象中
然后在将结果保存在捕获的对象.
示例图:
示例项目
如果你想通过自己的手来探索框架, 请您在实例项目上工作, 但是为了让我们专注于讨论AVFoundation框架, 我附带了一个入门项目, 在继续之前, 请点击下载并快速查看.
示例项目是当前项目的基础, 里面包含了:
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个步骤组成:
- 创建一个捕获会话。
- 获取和配置必要的捕获设备。
- 使用捕获设备创建输入。
- 配置照片输出对象以处理拍摄的图像。
我们将使用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
班级。我们现在要添加frontCamera
和rearCamera
属性,因为我们将设置多摄像头捕获的基础知识,并实现稍后更改摄像头的功能。
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 }
这就是我们刚刚做的:
- 这两行代码用于
AVCaptureDeviceDiscoverySession
查找当前设备上可用的所有广角相机,并将其转换为非可选AVCaptureDevice
实例的数组。如果没有相机可用,我们会抛出一个错误。 - 该循环通过代码段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 }
以下是我们所做的:
- 这条线只是确保
captureSession
存在。如果没有,我们会抛出一个错误。 - 这些
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文件格式的视频编解码器。然后,它增加photoOutput
了captureSession
。最后,它开始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()
这就是我们刚刚做的:
- 此
guard
声明确保在尝试切换摄像机之前我们有一个有效的正在运行的捕获会话。它还验证是否有一个当前活动的摄像头。 - 这一行告诉捕获会话开始配置。
- 这条
switch
语句调用switchToRearCamera
或者switchToFrontCamera
,取决于哪个摄像头当前处于活动状态。 - 这条线在配置之后提交或保存我们的捕获会话。
我们现在要做的就是实现switchToFrontCamera
和switchToRearCamera
:
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 }
两个函数都有非常相似的实现。他们首先获得捕获会话中所有输入的数组,并确保可以切换到请求相机。接下来,他们创建必要的输入设备,删除旧的,并添加新的。最后,他们设定currentCameraPosition
让CameraController
班级知道变化。简单!回到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 }
这不是一个复杂的实现,但我们的代码还不能编译,因为我们没有定义photoCaptureCompletionBlock
和CameraController
不符合AVCapturePhotoCaptureDelegate
。首先,我们添加一个属性photoCaptureCompletionBlock
来CameraController
:
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 开发者