web-dev-qa-db-ja.com

Swift 3.0でAVFoundationを使用してバーコードまたはQRコードをスキャンする

私はこれをフォローしています tutorial Swift 2.0 to 3.0。しかし、アプリケーションを起動したとき、アプリは動作しません!つまり、何も起こりません!ここに私のコードがあります:

ViewController:

class ViewController: UIViewController ,BarcodeDelegate {

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        let barcodeViewController: BarcodeViewController = segue.destination as! BarcodeViewController
        barcodeViewController.delegate = self

    }



    func barcodeReaded(barcode: String) {
        codeTextView.text = barcode
        print(barcode)
    }

}

バーコードVC:

import AVFoundation


protocol BarcodeDelegate {

    func barcodeReaded(barcode: String)
}

class BarcodeViewController: UIViewController,AVCaptureMetadataOutputObjectsDelegate {

    var delegate: BarcodeDelegate?
    var captureSession: AVCaptureSession!
    var code: String?


    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        print("works")

        self.captureSession = AVCaptureSession();
        let videoCaptureDevice: AVCaptureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)

        do {

            let videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)

            if self.captureSession.canAddInput(videoInput) {
                self.captureSession.addInput(videoInput)
            } else {
                print("Could not add video input")
            }

            let metadataOutput = AVCaptureMetadataOutput()
            if self.captureSession.canAddOutput(metadataOutput) {
                self.captureSession.addOutput(metadataOutput)

                metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
                metadataOutput.metadataObjectTypes = [AVMetadataObjectTypeQRCode,AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypePDF417Code]
            } else {
                print("Could not add metadata output")
            }

            let previewLayer = AVCaptureVideoPreviewLayer(session: self.captureSession)
            previewLayer?.frame = self.view.layer.bounds
            self.view.layer .addSublayer(previewLayer!)
            self.captureSession.startRunning()
        } catch let error as NSError {
            print("Error while creating vide input device: \(error.localizedDescription)")
        }



    }



    //I THINK THIS METHOD NOT CALL !
    private func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!) {

        // This is the delegate'smethod that is called when a code is readed
        for metadata in metadataObjects {
            let readableObject = metadata as! AVMetadataMachineReadableCodeObject
            let code = readableObject.stringValue

            // If the code is not empty the code is ready and we call out delegate to pass the code.
            if  code!.isEmpty {
                print("is empty")

            }else {

                self.captureSession.stopRunning()
                self.dismiss(animated: true, completion: nil)
                self.delegate?.barcodeReaded(barcode: code!)


            }
        }

    }

出力は次のとおりです。

2016-09-17 18:10:26.000919 BarcodeScaning [2610:674253] [MC]systemgroup.com.Apple.configurationprofilesパスのシステムグループコンテナは/private/var/containers/Shared/SystemGroup/systemgroup.com.Apple.configurationprofiles2016-09-17 18:10:26.007782 BarcodeScaning [2610:674253] [MC]有効な公開ユーザー設定からの読み取り。

14
Mc.Lover

最初のステップは、iOS 10の新しい要件であるユーザープライベートデータ型へのアクセスを宣言する必要があります。これは、目的の文字列と共にアプリの_Info.plist_に使用キーを追加することで実行できます。

次のフレームワークのいずれかを使用していて、使用法の宣言に失敗すると、最初にアクセスしたときにアプリがクラッシュするためです。

連絡先、カレンダー、リマインダー、写真、Bluetooth共有、マイク、カメラ、場所、ヘルス、HomeKit、メディアライブラリ、モーション、CallKit、音声認識、SiriKit、TVプロバイダー。

クラッシュを回避するには、提案されたキーを_Info.plist_に追加する必要があります。

enter image description here

そして、システムはユーザーにアクセスを許可するように要求するときに目的の文字列を表示します。

enter image description here

詳細については、この記事を使用できます。

以下に示すように、BarcodeViewControllerを少し修正して適切に動作するようにしました。

BarcodeViewController

_import UIKit
import AVFoundation

protocol BarcodeDelegate {
   func barcodeReaded(barcode: String)
}

class BarcodeViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {

   var delegate: BarcodeDelegate?

   var videoCaptureDevice: AVCaptureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
   var device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
   var output = AVCaptureMetadataOutput()
   var previewLayer: AVCaptureVideoPreviewLayer?

   var captureSession = AVCaptureSession()
   var code: String?

   override func viewDidLoad() {
      super.viewDidLoad()

      self.view.backgroundColor = UIColor.clear
      self.setupCamera()
   }

   private func setupCamera() {

      let input = try? AVCaptureDeviceInput(device: videoCaptureDevice)

      if self.captureSession.canAddInput(input) {
          self.captureSession.addInput(input)
      }

      self.previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)

      if let videoPreviewLayer = self.previewLayer {
          videoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
          videoPreviewLayer.frame = self.view.bounds
          view.layer.addSublayer(videoPreviewLayer)
      }

      let metadataOutput = AVCaptureMetadataOutput()
      if self.captureSession.canAddOutput(metadataOutput) {
          self.captureSession.addOutput(metadataOutput)

          metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
          metadataOutput.metadataObjectTypes = [AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN13Code]
      } else {
          print("Could not add metadata output")
      }
   }

   override func viewWillAppear(_ animated: Bool) {
       super.viewWillAppear(animated)

       if (captureSession.isRunning == false) {
          captureSession.startRunning();
       }
   }

   override func viewWillDisappear(_ animated: Bool) {
      super.viewWillDisappear(animated)

      if (captureSession.isRunning == true) {
         captureSession.stopRunning();
      }
   }

   func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
       // This is the delegate's method that is called when a code is read
       for metadata in metadataObjects {
           let readableObject = metadata as! AVMetadataMachineReadableCodeObject
           let code = readableObject.stringValue


           self.dismiss(animated: true, completion: nil)
           self.delegate?.barcodeReaded(barcode: code!)
           print(code!)
       }
   }
}
_

重要なポイントの1つは、グローバル変数を宣言し、viewWillAppear(:)およびviewWillDisappear(:)メソッド内でcaptureSessionを開始および停止することでした。以前のコードでは、バーコードを処理するメソッド内に決して入らないため、まったく呼び出されなかったと思います。

これがお役に立てば幸いです。

23
Victor Sigler

Victor Sigler's answerSwift 4に更新されました。その他の改良。

AVCaptureMetadataOutputObjectsDelegateのメソッドが

captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!)

metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection)

import UIKit
import AVFoundation

protocol BarcodeDelegate: class {
    func barcodeRead(barcode: String)
}

class ScannerViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
    weak var delegate: BarcodeDelegate?

    var output = AVCaptureMetadataOutput()
    var previewLayer: AVCaptureVideoPreviewLayer!

    var captureSession = AVCaptureSession()

    override func viewDidLoad() {
        super.viewDidLoad()

        setupCamera()
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        DispatchQueue.global(qos: .background).async {
            if !self.captureSession.isRunning {
                self.captureSession.startRunning()
            }
        }
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

        DispatchQueue.global(qos: .background).async {
            if self.captureSession.isRunning {
                self.captureSession.stopRunning()
            }
        }
    }

    fileprivate func setupCamera() {
        guard let device = AVCaptureDevice.default(for: .video),
            let input = try? AVCaptureDeviceInput(device: device) else {
            return
        }

        DispatchQueue.global(qos: .background).async {
            if self.captureSession.canAddInput(input) {
                self.captureSession.addInput(input)
            }

            let metadataOutput = AVCaptureMetadataOutput()

            if self.captureSession.canAddOutput(metadataOutput) {
                self.captureSession.addOutput(metadataOutput)

                metadataOutput.setMetadataObjectsDelegate(self, queue: .global(qos: .background))

                if Set([.qr, .ean13]).isSubset(of: metadataOutput.availableMetadataObjectTypes) {
                    metadataOutput.metadataObjectTypes = [.qr, .ean13]
                }
            } else {
                print("Could not add metadata output")
            }

            self.previewLayer = AVCaptureVideoPreviewLayer(session: self.captureSession)
            self.previewLayer.videoGravity = .resizeAspectFill

            DispatchQueue.main.async {
                self.previewLayer.frame = self.view.bounds
                self.view.layer.addSublayer(self.previewLayer)
            }
        }
    }

    func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
        // This is the delegate's method that is called when a code is read
        for metadata in metadataObjects {
            if let readableObject = metadata as? AVMetadataMachineReadableCodeObject,
                let code = readableObject.stringValue {
                dismiss(animated: true)
                delegate?.barcodeRead(barcode: code)
                print(code)
            }
        }
    }
}
8
Tamás Sengel

Swift 4すべてのコードタイプに対して4)

以下では、iOSでのバーコードスキャンによるいくつかのアイデアを共有したいと思います。

  • viewロジックからバーコードスキャナーロジックを分離し、
  • .plistファイルにエントリを追加します
  • exposurePointOfInterestおよびfocusPointOfInterestを設定します
  • 適切に変換されたCGRectでrectOfInterestsを設定します
  • focusModeおよびexposureModeを設定します
  • カメラキャプチャ設定の変更中に、captureDeviceをlockForConfigurationで適切にロックします

.plistファイルにエントリを追加
Info.plistファイルに次のコードを追加して、アプリケーションがiPhoneのカメラにアクセスできるようにします。

<key>NSCameraUsageDescription</key>
<string>Allow access to camera</string>

ExposurePointOfInterestおよびfocusPointOfInterestを設定します
exposurePointOfInterestおよびfocusPointOfInterestを使用すると、スキャンの品質が向上し、画面の中心点でカメラの焦点が速くなります。

rectOfInterestsを設定
このプロパティにより、カメラは画面の一部だけに焦点を合わせることができます。このようにして、コードをより速くスキャンし、画面の中央に表示されたコードだけに焦点を当てることができます。これは、他のコードがバックグラウンドで利用できない場合に便利です。

focusModeおよびExposureModeを設定しますプロパティは次のように設定する必要があります。

device.focusMode = .continuousAutoFocus
device.exposureMode = .continuousAutoExposure

これにより、連続的に焦点を合わせ、スキャンコードに合わせて適切に露出を設定できます。

デモ

ここで、このアイデアを実装する準備ができたプロジェクトを見つけることができます: https://github.com/lukszar/QuickScanner

3
lukszar
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
  print("caught QR code")
  for metadata in metadataObjects {
     let readableObject = metadata as! AVMetadataMachineReadableCodeObject
     let code = readableObject.stringValue
     if  code!.isEmpty {
        print("is empty")
     } else {
        self.captureSession.stopRunning()
        self.dismiss(animated: true, completion: nil)
        self.delegate?.gotQRCode(code: code!)
     }
  }
}

メソッドのシグネチャがSwift 3.で少し変更されたようです。

1
Mikhail Baynov

NSCameraUsageDescriptionをInfo.plistファイルに追加して、機能させる必要があります!

info.plistに行を追加し、NSCameraUsageDescriptionを新しく作成した行に入力し、アプリ内でカメラへのアクセスが必要な理由をユーザーに知らせるための文字列を追加します。

これでうまくいくはずです!

1
Johan Tingbacke