LoginSignup
85

More than 5 years have passed since last update.

iOSのバーコードリーダーで読み取り範囲を設定する

Last updated at Posted at 2015-07-29

1次元バーコードリーダーを使ったiPhoneアプリを作ろうとした時に,カメラの全画面でなく特定の範囲のみを読み取る設定をするだけのことで案外詰まった。
検索してもなかなか出てこなかったので共有。

環境

OS X 10.10.3
Xcode 6.4
iOS 8.4
iPhone6(実機でのみ動作)

カメラ画像の表示にはAVCaptureVideoPreviewLayerを
読み取りにはAVFoundationを使用

読み取り範囲を指定する

// metadata取得に必要な初期設定
let metaOutput = AVCaptureMetadataOutput();
metaOutput.setMetadataObjectsDelegate(self, queue: dispatch_get_main_queue());
self.captureSession.addOutput(metaOutput);

// バーコードリーダーの読み取り範囲を設定する
metaOutput.rectOfInterest = CGRectMake(y, 1-x-width, height, width)

AVCaptureMetadataOutputのrectOfInterestにCGRectを渡すことで解析範囲を 0 ~ 1.0 の範囲で指定する。
metaOutputの初期設定ではカメラの向きが横(landscape)になっているためパラメータの位置関係が通常と異なるということに気をつけなければならない。

デフォルトの位置関係

layout_default.png

metaOutputでの位置関係

layout_camera.png

ソースコード全容

Single View Applicationで作成した新規プロジェクトのViewControllerにコピペするだけでOK
読み取り可能範囲に赤い囲み線を表示し,読み取った内容をログに吐き出している
https://github.com/tomosooon/barcode-reader-sample

ViewController.swift
import UIKit
import AVFoundation

class ViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
    let captureSession = AVCaptureSession();
    var previewLayer: AVCaptureVideoPreviewLayer?
    var captureDevice: AVCaptureDevice?

    // 読み取り範囲(0 ~ 1.0の範囲で指定)
    let x: CGFloat = 0.1
    let y: CGFloat = 0.4
    let width: CGFloat = 0.8
    let height: CGFloat = 0.2

    override func viewDidLoad() {
        super.viewDidLoad()

        // カメラがあるか確認し,取得する
        self.captureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
        var error : NSError?
        let inputDevice = AVCaptureDeviceInput(device: self.captureDevice, error: &error)
        if let inp = inputDevice {
            self.captureSession.addInput(inp)
        } else {
            println(error)
        }

        // カメラからの取得映像を画面全体に表示する
        self.previewLayer = AVCaptureVideoPreviewLayer(session: self.captureSession)
        self.previewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
        self.previewLayer?.frame = CGRectMake(0, 0, self.view.bounds.width, self.view.bounds.height)
        self.view.layer.insertSublayer(self.previewLayer, atIndex: 0)

        // metadata取得に必要な初期設定
        let metaOutput = AVCaptureMetadataOutput();
        metaOutput.setMetadataObjectsDelegate(self, queue: dispatch_get_main_queue());
        self.captureSession.addOutput(metaOutput);

        // どのmetadataを取得するか設定する
        metaOutput.metadataObjectTypes = [AVMetadataObjectTypeEAN13Code];

        // どの範囲を解析するか設定する
        metaOutput.rectOfInterest = CGRectMake(y, 1-x-width, height, width)

        // 解析範囲を表すボーダービューを作成する
        let borderView = UIView(frame: CGRectMake(x * self.view.bounds.width, y * self.view.bounds.height, width * self.view.bounds.width, height * self.view.bounds.height))
        borderView.layer.borderWidth = 2
        borderView.layer.borderColor = UIColor.redColor().CGColor
        self.view.addSubview(borderView)

        // capture session をスタートする
        self.captureSession.startRunning()
    }

    // 映像からmetadataを取得した場合に呼び出されるデリゲートメソッド
    func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!) {

        // metadataが複数ある可能性があるためfor文で回す
        for data in metadataObjects {
            if data.type == AVMetadataObjectTypeEAN13Code {
                // 読み取りデータの全容をログに書き出す
                println("読み取りデータ:\(data)")
                println("データの文字列:\(data.stringValue)")
                println("データの位置:\(data.bounds)")
            }
        }
    }
}

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
85