iOSでQRコードを読み取ってみる

iOS7からQRコードの読み取りが簡単になっていたらしいのですが、そもそも最近はQRコードをあんまり見なくなったのでした。
思い立って(?)、QRコード読み取りのアプリを作ってみたのでメモ。あ、言語はObjective-Cです。

プロジェクト作成

いつもどおり、[New Project]から、[Single View Application]を選択、LanugageはObjective-C.
プロジェクトの[General] > [Deployment Info] > [Device Orientation]でPortraitだけにしておく(面倒なので)。
今日現在でiOS 8.2がでてますが、iPhone6を8.1からアップデートしてなかったので[Deployment Target]を8.1にしておいた。

カメラプレビューはView全体を使っちゃうので、storyboardは特に変更なし。

ViewController.h

カメラ入出力を扱うのでAVFoundation関係のimportと、画像認識のメタデータを扱うdelegateが必要です。
あとカメラのセッションをインスタンスで持っておかないと使えないので、これもプロパティを用意しておきます。

1
2
3
4
5
6
7
8
9

#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>

@interface ViewController : UIViewController <AVCaptureMetadataOutputObjectsDelegate>

@property (strong, nonatomic) AVCaptureSession* session;

@end

ViewController.m

viewDidload

とりあえずviewDidLoadでカメラ周りのインスタンスを生成しつつ、sessionを作成します。
本当はカメラが使えるかー?とかの判定があるべきですが、カメラある前提で書いていきます。
全コードをまず書いてしまうと、こんな感じ。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
- (void)viewDidLoad {
[super viewDidLoad];

// Device
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
// session
self.session = [[AVCaptureSession alloc] init];

// Input
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
if (input) {
[self.session addInput:input];
} else {
NSLog(@"error");
}

// Output
AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];
[self.session addOutput:output];
[output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
[output setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN13Code]];


// Preview
AVCaptureVideoPreviewLayer *preview = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
preview.videoGravity = AVLayerVideoGravityResizeAspectFill;
preview.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
[self.view.layer insertSublayer:preview atIndex:0];

// Start
[self.session startRunning];
}

ビデオデバイスとセッション、inputを作る。
previewや[self.session startRunning]特筆することはなく、カメラを使いたいときのいつもの感じです。

QRコード認識

delegateを受けておいて、delegateメソッドで認識した時の処理を定義します。
@[AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN13Code]を指定しているとこでQRコードの認識を設定。

1
2
3
4
5
// Output
AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];
[self.session addOutput:output];
[output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
[output setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN13Code]];

認識時処理

認識したらcaptureOutputがdelegateメソッドで呼ばれるので、読み取ります。
[(AVMetadataMachineReadableCodeObject *)data stringValue]で文字列化しています。
顔認識の場合とか、他の識別が動作した場合もこのメソッドが呼ばれてしまうので、data.typeAVMetadataObjectTypeQRCode
ときだけ読み取り文字列をURLとしてSafariで開くようにしています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects
fromConnection:(AVCaptureConnection *)connection
{
// 複数のmetadataが来るので順に調べる
for (AVMetadataObject *data in metadataObjects) {
if (![data isKindOfClass:[AVMetadataMachineReadableCodeObject class]]) continue;
// QR code data
NSString *strValue = [(AVMetadataMachineReadableCodeObject *)data stringValue];
// type ?
if ([data.type isEqualToString:AVMetadataObjectTypeQRCode]) {
// QRコードの場合
NSURL *url = [NSURL URLWithString:strValue];
if ([[UIApplication sharedApplication] canOpenURL:url]) {
[[UIApplication sharedApplication] openURL:url];
}
}
}
}

試してみる

試しにこのブログのURLをQRコードにしてみた。識別されたらこのブログを自動的にSafariで開きます。

qrcode