Goalist Developers Blog

ささっとカメラアプリを作ってみた@Swift

コンニチハ。iOSエンジニアの渡部です。
iPhone8の情報がちょいちょい出てきましたね。
そろそろAppleの社員がバーにiPhoneを置き忘れていく時期です。
何それ?な人はググってみてください。

f:id:watabe1028:20170119152445j:plain

画像はフリー素材アイドルさんをお借りしました。

iPhoneユーザで誰もが使う機能、カメラ。
snowのような顔認識やフィルター機能はなかなかハードルが高いですが
カメラ機能を実装するのは結構簡単です。

今日はカメラアプリを作ってみます。

写真を撮って保存するシンプルなカメラです。

手順

1.info.plistでカメラが使えるように設定する
2.必要なオブジェクトを設置
3.ソースをガリガリ

カメラアプリの実行、デバッグは実機でしかできないので注意です。
ソースをガリガリといってもだいたいコピペでできますのでご安心を。
自分はファミマのミニクッキーを食べながら失礼します。

手順にプロジェクト作成は含めません。
今回もSingle View Applicationで作成します。

1.info.plistでカメラが使えるように設定する

これはそのままです。 スクショ通りに追加してください。

f:id:watabe1028:20170119152918p:plain

右側のテキストはカメラ許可を取る際に表示するメッセージです。
ここは好きな文言を設定してください。

2.必要なオブジェクトを設置

Storyboardで設置していきます。

f:id:watabe1028:20170119153153p:plain

UIViewとUIButtonを画像の通りに設定します。
UIViewはわかりやすいようにlightgrayにしてあります。

UIViewをOutletで、UIButtonをActionでソースに紐づけます。

3.ソースをガリガリ

あとはガリガリ書きます。

まずはimport、Outlet、Actionの追加です。
デリゲートの追加も忘れずに。

import UIKit
import AVFoundation

class ViewController: UIViewController, AVCapturePhotoCaptureDelegate {
    
    @IBOutlet weak var cameraView: UIView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    /// 撮影ボタン押下時に呼ばれる
    @IBAction func cameraButtonTapped(_ sender: Any) {
    }

カメラ起動をviewDidLoadに記述します。
本当はviewDidAppearの方が適切だと思われますが今回は(面倒なので)おけーです。
viewDidLoadの前にAVCaptureSessionたちも追加します。
これがないと起動しません。

    var captureSesssion: AVCaptureSession!
    var stillImageOutput: AVCapturePhotoOutput?
    var previewLayer: AVCaptureVideoPreviewLayer?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        captureSesssion = AVCaptureSession()
        stillImageOutput = AVCapturePhotoOutput()
        
        // 解像度の設定
        captureSesssion.sessionPreset = AVCaptureSessionPreset1920x1080
        
        let device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
        
        do {
            let input = try AVCaptureDeviceInput(device: device)
            
            // 入力
            if (captureSesssion.canAddInput(input)) {
                captureSesssion.addInput(input)
                
                // 出力
                if (captureSesssion.canAddOutput(stillImageOutput)) {
                    
                    // カメラ起動
                    captureSesssion.addOutput(stillImageOutput)
                    captureSesssion.startRunning()
                    
                    // アスペクト比、カメラの向き(縦)
                    previewLayer = AVCaptureVideoPreviewLayer(session: captureSesssion)
                    previewLayer?.videoGravity = AVLayerVideoGravityResizeAspect // アスペクトフィット
                    previewLayer?.connection.videoOrientation = AVCaptureVideoOrientation.portrait
                    
                    cameraView.layer.addSublayer(previewLayer!)
                    
                    // ビューのサイズの調整
                    previewLayer?.position = CGPoint(x: self.cameraView.frame.width / 2, y: self.cameraView.frame.height / 2)
                    previewLayer?.bounds = cameraView.frame
                }
            }
        }
        catch {
            print(error)
        }
    }

撮影ボタンです。
ボタン押下で撮影します。

     /// 撮影ボタン押下時に呼ばれる
    @IBAction func cameraButtonTapped(_ sender: Any) {
        
        // カメラの設定
        let settingsForMonitoring = AVCapturePhotoSettings()
        settingsForMonitoring.flashMode = .auto
        settingsForMonitoring.isAutoStillImageStabilizationEnabled = true
        settingsForMonitoring.isHighResolutionPhotoEnabled = false
        
        // 撮影
        stillImageOutput?.capturePhoto(with: settingsForMonitoring, delegate: self)
    }

最後に写真の保存です。
今回はベタにJPEG形式で保存してます。

    /// カメラで撮影完了時にフォトライブラリに保存
    func capture(_ captureOutput: AVCapturePhotoOutput, didFinishProcessingPhotoSampleBuffer photoSampleBuffer: CMSampleBuffer?, previewPhotoSampleBuffer: CMSampleBuffer?, resolvedSettings: AVCaptureResolvedPhotoSettings, bracketSettings: AVCaptureBracketedStillImageSettings?, error: Error?) {
        
        if let photoSampleBuffer = photoSampleBuffer {
            
            // JPEG形式で画像データを取得
            let photoData = AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer: photoSampleBuffer, previewPhotoSampleBuffer: previewPhotoSampleBuffer)
            
            let image = UIImage(data: photoData!)
            
            // フォトライブラリに保存
            UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil)
        }
    }

少しソースが多いですがまるっとコピペでちゃんと動きます。

起動すると・・・ f:id:watabe1028:20170119155143p:plain

先ほど「1.info.plistでカメラが使えるように設定する」で設定したメッセージが表示されます。 もちろん許可してください。

画面はこんな感じ。 f:id:watabe1028:20170119155209p:plain

撮影すると保存の許可を求めるメッセージが表示されます。 これも「1.info.plistでカメラが使えるように設定する」で設定したものです。 f:id:watabe1028:20170119155317p:plain

ちゃんと保存されてます。 f:id:watabe1028:20170119155343j:plain

まとめ

今回はカメラの起動から写真の保存まででしたが
売れるアプリにするには一工夫ないとダメです。
いずれ顔認識や色々なフィルターの記事も書こうと思います。

今まさにプライベートでカメラアプリを作っていたので
いい復習になりました。
ファミマのお菓子最高ですね。では。