【Swift/SwiftUI】OSのカメラを起動する

目次

概要

アプリは端末内のみで完結するものだけではない。
画像撮影や文字認識、写真加工、ARなど、カメラを使ったアプリがたくさんある。
そのため、今回はカメラの起動をするところをやってみよう。

ソースコード

Xcodeの設定

カメラを使用する場合は、アプリからカメラへアクセスするための設定をする必要がある。
カメラへアクセスする際は「Privacy – Camera Usage Description」を付け加える。
そして、Valueのところはカメラへのアクセスを許可するかどうかのダイアログに表示される文言でもある。

アプリ起動部分

これは特に説明することはない。

import SwiftUI

@main
struct TestApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    var body: some Scene {
        WindowGroup {
            CameraLaunchView()
        }
    }
}

View表示部分

以下のUIを表示する・

  • 撮影した画像を表示する(撮影した画像データがない場合、デフォルトのUIとして何か表示する)
  • カメラを起動するためのボタンを設置する
import SwiftUI

struct CameraLaunchView: View {
    @ObservedObject private var viewModel = CameraLaunchViewModel()
    
    var body: some View {
        VStack(alignment: .center) {
            ZStack {
                Rectangle()
                    .fill(Color.gray.opacity(0.5))
                    .padding()
                Text("P h o t o")
                    .font(.system(size: 30))
                    .bold()
                    .foregroundColor(Color.white)
                if viewModel.imageData.count > 0, let uiImage = UIImage(data: viewModel.imageData) {
                    Image(uiImage: uiImage)
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .padding()
                }
            }
            Button {
                viewModel.isLaunchedCamera.toggle()
            } label: {
                Text("カメラ起動")
            }
        }
        .fullScreenCover(isPresented: $viewModel.isLaunchedCamera) {
            Imagepicker(show: $viewModel.isLaunchedCamera,
                        image: $viewModel.imageData)
        }
    }
}

処理部分

ほぼほぼ変数も持つためのもの。
本来なら写真を取り込んだあとに処理を書いていくが、今回はただ単に撮影した画像を表示するだけなので簡単。

import Foundation

class CameraLaunchViewModel: ObservableObject {
    @Published var isLaunchedCamera = false    
    @Published var imageData = Data(capacity: 0)
}

カメラ起動関連

今回の根幹。
詳しくは詳細に記載する。

import Foundation
import UIKit
import SwiftUI

struct Imagepicker : UIViewControllerRepresentable {
    @Binding var show: Bool
    @Binding var image: Data
    
    func makeCoordinator() -> Imagepicker.Coodinator {
        return Imagepicker.Coordinator(parent: self)
    }
      
    func makeUIViewController(context: UIViewControllerRepresentableContext<Imagepicker>) -> UIImagePickerController {
        let controller = UIImagePickerController()
        controller.sourceType = .camera
        controller.delegate = context.coordinator
        return controller
    }
    
    func updateUIViewController(_ uiViewController: UIImagePickerController,
                                context: UIViewControllerRepresentableContext<Imagepicker>) {
    }
    
    class Coodinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
        var parent : Imagepicker
        
        init(parent : Imagepicker){
            self.parent = parent
        }
        
        func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
            self.parent.show.toggle()
        }
        
        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            
            guard let image = info[.originalImage] as? UIImage else {
                return
            }
            guard let data = getCorrectOrientationUIImage(uiImage: image).jpegData(compressionQuality: 1.0) else {
                return
            }
            self.parent.image = data
            self.parent.show.toggle()
        }
        
        private func getCorrectOrientationUIImage(uiImage:UIImage) -> UIImage {
            var editedImage = UIImage()
            let ciContext = CIContext(options: nil)
            
            switch uiImage.imageOrientation {
            case .left:
                guard let orientedCIImage = CIImage(image: uiImage)?.oriented(.left),
                      let cgImage = ciContext.createCGImage(orientedCIImage, from: orientedCIImage.extent) else {
                    return uiImage
                }
                editedImage = UIImage(cgImage: cgImage)
            case .down:
                guard let orientedCIImage = CIImage(image: uiImage)?.oriented(.down),
                      let cgImage = ciContext.createCGImage(orientedCIImage, from: orientedCIImage.extent) else {
                    return uiImage
                }
                editedImage = UIImage(cgImage: cgImage)
            case .right:
                guard let orientedCIImage = CIImage(image: uiImage)?.oriented(.right),
                      let cgImage = ciContext.createCGImage(orientedCIImage, from: orientedCIImage.extent) else {
                    return uiImage
                }
                editedImage = UIImage(cgImage: cgImage)
            default:
                editedImage = uiImage
            }
            
            return editedImage
        }
    }
}

デモ動画

詳細

getCorrectOrientationUIImageメソッド

このメソッドでは端末の向きに応じて撮影した画像の向きを調整する。
OSのカメラでは、どうやら端末の向きを調整しないと想定とは違う向きで画像を格納してしまうらしい。
そのためにもこのメソッドが必要。

uiImage.imageOrientationで画像の向きを取得して、通常時から左右にに90°回したとき、または逆さにしたときを取得して、それに応じて画像の向きを回転させるというもの。

CIImageは画像にフィルターをかける時などに使われる。