開発部 iOS 担当の金森です。

前回 に引き続いて、今度は iOS11 で新しく追加された機能を利用した QRコード の生成/読み取りを行ってみます。
コードの一部は前回から流用しています。


注意事項

iOS11 の内容はリリースされるまで、今後変更される可能性があります。
そのため、本記事でも iOS11 についての記載は執筆時点で判明していた情報として参照いただけますようお願いします。


対象読者

iOS アプリで QRコード を使って遊んでみたいと思っている人


動作環境

Xcode 9 beta2
Deployment target iOS 11.0
Swift 3


今回使うフレームワーク

  1. Vision.framework
  2. CoreImage.framework

また、本記事の内容は WWDC2017 での セッションビデオ (Barcode -> 35:00 頃) を参考にしています。


Vision フレームワークによるバーコードの検出


前回 AVFoundation を使った QRコード の検出を行いました。
今回との大きな違いは、「キャプチャ中に検出するかどうか」です。
AVFoundation での実現方法がカメラに映ったものを読み取る方式なのに対し、
Vision は画像を対象に QRコード を検出します。


処理フロー

  1. VNImageRequestHandler (以下、Handler) を生成
  2. Handler の生成時に解析対象とする画像を設定
  3. 画像に対する Request を生成
    1. 目的にあった Request を生成する
    2. QRコード(バーコード)を検出したい → VNDetectBarcodesRequest
  4. Handler に対して Request を実行
  5. Request オブジェクトに結果 (Observation) が設定される
  6. 結果から CIBarcodeDescriptor を取り出す
  7. 期待する Barcode の種類に cast してデータを取得
  8. ※画像選択にはアルバムを利用するため、 Privacy 設定を入れておく

上記の流れをざっくり実装すると以下のようになります。
let handler = VNImageRequestHandler(cgImage: image.cgImage, options: [:])
let request = VNDetectBarcodesRequest()
try? handler.perform([request])
let results = request.results as? [VNBarcodeObservation]


検出した QRコード からデータを取り出す場合は以下のようにします。

for result in results {
    if let descriptor = result.barcodeDescriptor as? CIQRCodeDescriptor {
        let data = descriptor.errorCorrectedPayload
        ..
    }
}


データを取り出すのにわざわざキャストしないといけないのはやや面倒に感じますね。
なお、現在用意されている Descriptor は以下の 4 つだけのようです。
  • CIQRCodeDescriptor
  • CIAztecCodeDescriptor
  • CIPDF417CodeDescriptor
  • CIDataMatrixCodeDescriptor


QRコード の生成

以下では Descriptor を利用した方法を説明します。

処理フロー

  1. CIFilter を QRコード 向けに作成
  2. パラメータに以下を指定
    1. CIBarcodeDescriptor
  3. CIContext から CGImage を生成
  4. CGImage をもとに UIImage を描画
  5. UIImage (QRコード) を適当な大きさで表示

ほぼほぼ前回と同じなので特に解説するところもありませんが、パラメータ指定時の定数が変わっているので注意してください。
private func generateCode(descriptor: CIBarcodeDescriptor) -> CGImage? {
    let params: [String:Any] = [
        "inputBarcodeDescriptor": descriptor
    ]
    let filter = CIFilter(name: "CIBarcodeGenerator", withInputParameters: params)
    guard let image = filter?.outputImage else {
        return nil
    }
    return CIContext().createCGImage(image, from: image.extent)
}


これで QRコード が生成できます。
前回に比べるとパラメータが descriptor としてまとまった分、楽になっていますね!
(誤差の範囲)
今回のサンプル実装は読み取った QRコード から同じ QRコード 画像を作成しているだけですが、
CIQRCodeDescriptor は新しく生成することもできます。
パラメータ文字列を指定するよりもこちらを使った方が扱いやすくはなりそうです。


おわりに


QRコード の生成/読み取りについて iOS11 で追加された方法を説明しました。
バーコードの検出/生成に新しい側面での仕組みが追加された形なので、状況に応じて使い分けるのが良さそうです。
ただ、個人的には画像からバーコードを検出する用途が思い浮かばないので出番はまだまだ先になりそうです。。。

コード例の全体については以下を参照してください。
https://github.com/eq-inc/eq-ios-qrcode(iOS11)

コメントの投稿