post Image
[iOS10] カメラを触ってみる

はじめに

久しぶりにAVFoundation.frameworkを利用したカメラを触る機会がありました。
他にも、UIKit.framework(UIImagePickerController)を利用するパターンもあります。
参考記事

今更感はありますが、
iOS10で、非推奨になっているクラスがあったので、整理しておきます。

非推奨

Warningが出たものは下記の2箇所でした。

① AVCaptureStillImageOutput -> AVCapturePhotoOutput(iOS10.0〜)
撮影および、完了通知の仕組みが変わった。

〜iOS9
    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
        }
    }
iOS10〜
    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〜)
カメラのインスタンスの取得方法が変わった。

〜iOS9
    func findDevice(position: AVCaptureDevicePosition) -> AVCaptureDevice? {

        guard let device = AVCaptureDevice.devices().filter({
            ($0 as! AVCaptureDevice).position == position
        }).first as? AVCaptureDevice else {
            fatalError("カメラが見つかりません")
        }
        return device
    }
iOS10〜
    func findDevice(position: AVCaptureDevicePosition) -> AVCaptureDevice? {

        return AVCaptureDevice.defaultDevice(withDeviceType: .builtInWideAngleCamera,
                                             mediaType: AVMediaTypeVideo,
                                             position: position)
    }

サンプル

アプリ起動直後にカメラが起動し、
撮影ボタンを押下するとカメラロールに保存するだけのサンプルです。
Deployment Targetを10.0以上とします。

完成版のソースコードは、こちらも御覧ください。

実装手順

1. AVFoundation.frameworkを追加する

2. info.plistに下記を追加する

info.plist
    <key>NSCameraUsageDescription</key>
    <string>カメラへアクセスするために必要です</string>
    <key>NSPhotoLibraryUsageDescription</key>
    <string>アルバムへアクセスするために必要です</string>

3. カメラへアクセスするためのクラスを定義する

CameraUtil.swift
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周りは割愛します。

ViewController.swift
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は見るものの、実装する機会がないと見落としてしまいますね。
たまにシンプルな機能を実装してみるのも勉強になりました。

以上です。


『 Swift 』Article List
Category List

Eye Catch Image
Read More

Androidに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

AWSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Bitcoinに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

CentOSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

dockerに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

GitHubに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Goに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Javaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

JavaScriptに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Laravelに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Pythonに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Rubyに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Scalaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Swiftに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Unityに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Vue.jsに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Wordpressに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

機械学習に関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。