はじめに
久しぶりにAVFoundation.frameworkを利用したカメラを触る機会がありました。
他にも、UIKit.framework(UIImagePickerController)を利用するパターンもあります。
参考記事
今更感はありますが、
iOS10で、非推奨になっているクラスがあったので、整理しておきます。
非推奨
Warningが出たものは下記の2箇所でした。
① AVCaptureStillImageOutput -> AVCapturePhotoOutput(iOS10.0〜)
撮影および、完了通知の仕組みが変わった。
let imageOutput = AVCaptureStillImageOutput()
func takePhoto() {
let videoConnection = imageOutput.connection(withMediaType: AVMediaTypeVideo)
imageOutput.captureStillImageAsynchronously(
from: videoConnection,
completionHandler: { [weak self]
(imageDataBuffer, error) in
if let e = error {
print(e.localizedDescription)
return
}
self?.savePhoto(imageDataBuffer: imageDataBuffer!)
})
}
func savePhoto(imageDataBuffer: CMSampleBuffer) {
if let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(imageDataBuffer),
let image = UIImage(data: imageData) {
//TODO : do something
}
}
let imageOutput = AVCapturePhotoOutput()
func takePhoto() {
imageOutput.capturePhoto(with: AVCapturePhotoSettings(), delegate: self)
}
// MARK: - AVCapturePhotoCaptureDelegate
extension CameraUtil: AVCapturePhotoCaptureDelegate {
func capture(_ captureOutput: AVCapturePhotoOutput,
didFinishProcessingPhotoSampleBuffer photoSampleBuffer: CMSampleBuffer?,
previewPhotoSampleBuffer: CMSampleBuffer?,
resolvedSettings: AVCaptureResolvedPhotoSettings,
bracketSettings: AVCaptureBracketedStillImageSettings?,
error: Error?) {
if let error = error {
print(error.localizedDescription)
return
}
savePhoto(imageDataBuffer: photoSampleBuffer!)
}
}
func savePhoto(imageDataBuffer: CMSampleBuffer) {
if let imageData =
AVCapturePhotoOutput.jpegPhotoDataRepresentation(
forJPEGSampleBuffer: imageDataBuffer,
previewPhotoSampleBuffer: nil),
let image = UIImage(data: imageData) {
//TODO : do something
}
}
}
② AVCaptureDevice.devices() -> AVCaptureDeviceDiscoverySession(iOS10.0〜)
カメラのインスタンスの取得方法が変わった。
func findDevice(position: AVCaptureDevicePosition) -> AVCaptureDevice? {
guard let device = AVCaptureDevice.devices().filter({
($0 as! AVCaptureDevice).position == position
}).first as? AVCaptureDevice else {
fatalError("カメラが見つかりません")
}
return device
}
func findDevice(position: AVCaptureDevicePosition) -> AVCaptureDevice? {
return AVCaptureDevice.defaultDevice(withDeviceType: .builtInWideAngleCamera,
mediaType: AVMediaTypeVideo,
position: position)
}
サンプル
アプリ起動直後にカメラが起動し、
撮影ボタンを押下するとカメラロールに保存するだけのサンプルです。
Deployment Targetを10.0以上とします。
完成版のソースコードは、こちらも御覧ください。
実装手順
1. AVFoundation.frameworkを追加する
2. info.plistに下記を追加する
<key>NSCameraUsageDescription</key>
<string>カメラへアクセスするために必要です</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>アルバムへアクセスするために必要です</string>
3. カメラへアクセスするためのクラスを定義する
import UIKit
import AVFoundation
final class CameraUtil: NSObject {
let imageOutput = AVCapturePhotoOutput()
func findDevice(position: AVCaptureDevicePosition) -> AVCaptureDevice? {
return AVCaptureDevice.defaultDevice(withDeviceType: .builtInWideAngleCamera,
mediaType: AVMediaTypeVideo,
position: position)
}
func createView(session: AVCaptureSession?,
device: AVCaptureDevice?) -> AVCaptureVideoPreviewLayer?{
let videoInput = try! AVCaptureDeviceInput.init(device: device)
session?.addInput(videoInput)
session?.addOutput(imageOutput)
return AVCaptureVideoPreviewLayer.init(session: session)
}
func takePhoto() {
imageOutput.capturePhoto(with: AVCapturePhotoSettings(), delegate: self)
}
}
// MARK: - AVCapturePhotoCaptureDelegate
extension CameraUtil: AVCapturePhotoCaptureDelegate {
func capture(_ captureOutput: AVCapturePhotoOutput,
didFinishProcessingPhotoSampleBuffer photoSampleBuffer: CMSampleBuffer?,
previewPhotoSampleBuffer: CMSampleBuffer?,
resolvedSettings: AVCaptureResolvedPhotoSettings,
bracketSettings: AVCaptureBracketedStillImageSettings?,
error: Error?) {
if let error = error {
print(error.localizedDescription)
return
}
savePhoto(imageDataBuffer: photoSampleBuffer!)
}
func savePhoto(imageDataBuffer: CMSampleBuffer) {
if let imageData =
AVCapturePhotoOutput.jpegPhotoDataRepresentation(
forJPEGSampleBuffer: imageDataBuffer,
previewPhotoSampleBuffer: nil),
let image = UIImage(data: imageData) {
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
}
}
}
4. 使ってみる
UI周りは割愛します。
import UIKit
import AVFoundation
final class ViewController: UIViewController {
@IBOutlet weak var baseView: UIView!
let session = AVCaptureSession()
let camera = CameraUtil()
override func viewDidLoad() {
super.viewDidLoad()
setupCameraView()
session.startRunning()
}
private func setupCameraView() {
let device = camera.findDevice(position: .back)
if let videoLayer = camera.createView(session: session, device: device) {
videoLayer.frame = baseView.bounds
videoLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
baseView.layer.addSublayer(videoLayer)
} else {
fatalError("VideoLayerがNil")
}
}
@IBAction func photoDidTap(_ sender: UIButton) {
camera.takePhoto()
}
}
まとめ
API diffは見るものの、実装する機会がないと見落としてしまいますね。
たまにシンプルな機能を実装してみるのも勉強になりました。
以上です。