くま's Tech系Blog

基本的には技術で学んだことを書き留めようと思います。雑談もやるかもね!

iOSのQRコード読み取りについて

今回はiOSQRコードを読み取る処理について書こうかと思います

使用バージョンは以下の通りです

Swift 5.0

カメラ起動

QRコードを読み取る処理で最初にやることと言えば、カメラ起動です

まずは、カメラ起動の流れを見ていきましょう

カメラに起動にはAVFoundationというフレームワークを使います

カメラなどのデバイスを表すAVCaptureDeviceクラスで取得した入力データを、AVCaptureDeviceInputクラス(サブクラス)を用いてAVCaptureSessionに渡します

AVCaptureSessionクラスから出力されるデータは、出力形式を管理するAVCaptureOutputクラスによって、画像、動画、フレームデータ、音声データ、メターデータなど様々な形式で出力されます

つまりはAVCaptureSessionAVCaptureInputAVCaptureOutputを追加し、キャプチャのアクティビティ全体を管理するものです

まずは、デバイスの設定をしてセッションに追加します

カメラと言っても、背面カメラや前面カメラ、ビデオ撮影やフォト撮影など色々あるので、どのタイプのカメラを使うのかを設定します

class ViewController: UIViewController {
    // デバイスからの入力と出力を管理するオブジェクト
    var captureSession = AVCaptureSession()
    // キャプチャーの出力データを受け付けるオブジェクト
    var output = AVCaptureMetadataOutput?
   // カメラプレビュー表示用のレイヤ
   var cameraPreviewLayer: AVCaptureVideoPreviewLayer?

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // デバイス一覧を取得(ビデオを指定)
        let videoDevices = AVCaptureDevice.device(for: AVMediaType.video)
        
        //  カメラを前面で起動する場合の処理
        for device in videoDevices {
            let device = device
            if device.position ==  AVCaptureDevice.Position.front {
                let videoDevice =  device
           }
       }

  // 前面カメラからvideoInputを取得
  let videoInput : AVCaptureDeviceInput? =  try! AVCaptureDeviceInput(device: videoDevice!)

     // セッションに追加
       if videoInput != nil {
           captureSession.addInput(videoInput!)
           output = AVCaptureOutput()
           captureSession.addOutput(output!)
           output.addMetadataObjectsDelegate(self, queue: DispatchQueue.main)   
           output!.metadataObjectTypes = output!.availableMetadataObjectTypes
       }
    } 
}

次に、カメラの取得している映像の表示をします

ここでLayerが出てくるのですが、LayerはViewに描画する内容を管理するオブジェクトです

カメラの取得している映像を画面に表示するには、AVCaptureVideoPreviewLayerクラスを使います

class ViewController: UIViewController {
    // デバイスからの入力と出力を管理するオブジェクト
    var captureSession = AVCaptureSession()
    // キャプチャーの出力データを受け付けるオブジェクト
    var output = AVCaptureMetadataOutput?

    override func viewDidLoad() {
        super.viewDidLoad()
        
        //  デバイスの設定をしてセッションに追加した後の処理はここ

  self.cameraPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
       // キャプチャの縦横比を維持した状態で表示するように設定
       self.cameraPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
       // プレビューレイヤの表示の向きを設定
       self.cameraPreviewLayer?.connection?.videoOrientation = AVCaptureVideoOrientation.portrait
       self.cameraPreviewLayer?.frame = view.frame
  
  // カメラの向きを設定
  switch(UIApplication.shared.statusBarOrientation) {
       case UIInterfaceOrientation.portrait:
           self.cameraPreviewLayer?.connection?.videoOrientation = AVCaptureVideoOrientaion.portrait
       case UIInterfaceOrientation.portraitUpsideDown:
           self.cameraPreviewLayer?.connection?.videoOrientation = AVCaptureVideoOrientaion.portraitUpsideDown
       case UIInterfaceOrientation.landscapeLeft:
           self.cameraPreviewLayer?.connection?.videoOrientation = AVCaptureVideoOrientaion.landscapeLeft
       case UIInterfaceOrientation.landscapeRight:
           self.cameraPreviewLayer?.connection?.videoOrientation = AVCaptureVideoOrientaion.landscapeRight
       case .unknown:
           self.cameraPreviewLayer?.connection?.videoOrientation = AVCaptureVideoOrientaion.portrait
       @unknown default:
           self.cameraPreviewLayer?.connection?.videoOrientation = AVCaptureVideoOrientaion.portrait
        }

       self.view.layer.insertSublayer(self.cameraPreviewLayer!, at: 0)

  // セッションの開始
  captureSession.startRunning()
}

ここまででカメラの起動ができました

次にQRコードの読み取りを行う処理を実装します

QRコード読み取り

QRコードの読み取りはAVCaptureMetadataOutputObjectsDelegateで行います

extension ViewController: AVCaptureMetadataOutputObjectsDelegate {
    
    func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection){
        for object in metadataObjects {
            let data = object
            if !(data isKind(of: AVMetadataMachineReadableCodeObject.self)) {
                continue
            }

            // QRのデータの場合読み取りを行う
            if(data.type == AVMetadataObject.ObjectType.qr) {
                 // ここにスキャン後の処理を入れる

         captureSession.stopRunning()
                 break
            }
        }
    }
}

ここまででQRの読み取りが行えます

一連の流れを理解するのに時間がかかるかもしれないですが、カメラはiOS開発でよく使うと思うので、理解する必要があると思いました。

最後にカメラのアクセス許可をInfo.plistで設定する必要があります

keyにPrivacy - Camera Usage Descriptionを設定して、valueには理由などを記載します

こうすれば、カメラが起動するときにアクセス許可が求められるようになり、アプリが落ちることはなくなります

参照

Swiftでカメラアプリを作成する(1)

【メモ書き】Swift4でQRコードリーダーを作ろうとした時…