Edited at

【iOS】バーコードリーダーをSwiftで作ってみた

More than 1 year has passed since last update.

iOSのAVFoundationを使ってバーコードを読み取るやつを作ってみました。

調べて見つけた元ネタはこちらです。

iOS 7でバーコード・QRコードを読み取る方法と生成する方法(おまけもあるよ) - Qiita

上記の記事を参考にさせていただきました!

@hkato193さん、ありがとうございました!

上記の記事はObjective-Cで書かれているので、それをSwiftに書き直しました。


動画

2016/06/06 追記

なんかあれなんで動画作ってみました。


バーコードリーダーの動画作ってみた pic.twitter.com/cIGtj0f7n1

— ryokosuge (@ryo_kosuge) 2016年6月6日


ソース


ViewController.swift

import UIKit

import AVFoundation

final class ViewController: UIViewController {

@IBOutlet weak var captureView: UIView?

private lazy var captureSession: AVCaptureSession = AVCaptureSession()
private lazy var captureDevice: AVCaptureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
private lazy var capturePreviewLayer: AVCaptureVideoPreviewLayer = {
let layer = AVCaptureVideoPreviewLayer(session: self.captureSession)
return layer
}()

private var captureInput: AVCaptureInput? = nil
private lazy var captureOutput: AVCaptureMetadataOutput = {
let output = AVCaptureMetadataOutput()
output.setMetadataObjectsDelegate(self, queue: dispatch_get_main_queue())
return output
}()

override func viewDidLoad() {
super.viewDidLoad()

// Do any additional setup after loading the view, typically from a nib.
setupBarcodeCapture()
}

override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
capturePreviewLayer.frame = self.captureView?.bounds ?? CGRectZero
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

// MARK: - private
private func setupBarcodeCapture() {
do {
captureInput = try AVCaptureDeviceInput(device: captureDevice)
captureSession.addInput(captureInput)
captureSession.addOutput(captureOutput)
captureOutput.metadataObjectTypes = captureOutput.availableMetadataObjectTypes
capturePreviewLayer.frame = self.captureView?.bounds ?? CGRectZero
capturePreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
captureView?.layer.addSublayer(capturePreviewLayer)
captureSession.startRunning()
} catch let error as NSError {
print(error)
}
}

private func convartISBN(value: String) -> String? {
let v = NSString(string: value).longLongValue
let prefix: Int64 = Int64(v / 10000000000)
guard prefix == 978 || prefix == 979 else { return nil }
let isbn9: Int64 = (v % 10000000000) / 10
var sum: Int64 = 0
var tmpISBN = isbn9
/*
for var i = 10; i > 0 && tmpISBN > 0; i -= 1 {
let divisor: Int64 = Int64(pow(10, Double(i - 2)))
sum += (tmpISBN / divisor) * Int64(i)
tmpISBN %= divisor
}
*/

var i = 10
while i > 0 && tmpISBN > 0 {
let divisor: Int64 = Int64(pow(10, Double(i - 2)))
sum += (tmpISBN / divisor) * Int64(i)
tmpISBN %= divisor
i -= 1
}

let checkdigit = 11 - (sum % 11)
return String(format: "%lld%@", isbn9, (checkdigit == 10) ? "X" : String(format: "%lld", checkdigit % 11))
}
}

extension ViewController: AVCaptureMetadataOutputObjectsDelegate {

func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!) {
self.captureSession.stopRunning()
guard let objects = metadataObjects as? [AVMetadataObject] else { return }
var detectionString: String? = nil
let barcodeTypes = [AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeEAN13Code]
for metadataObject in objects {
loop: for type in barcodeTypes {
guard metadataObject.type == type else { continue }
guard self.capturePreviewLayer.transformedMetadataObjectForMetadataObject(metadataObject) is AVMetadataMachineReadableCodeObject else { continue }
if let object = metadataObject as? AVMetadataMachineReadableCodeObject {
detectionString = object.stringValue
break loop
}
}
var text = ""
guard let value = detectionString else { continue }
text += "読み込んだ値:\t\(value)"
text += "\n"
guard let isbn = convartISBN(value) else { continue }
text += "ISBN:\t\(isbn)"
resultTextLabel?.text = text
let URLString = String(format: "http://amazon.co.jp/dp/%@", isbn)
guard let URL = NSURL(string: URLString) else { continue }
UIApplication.sharedApplication().openURL(URL)
}
self.captureSession.startRunning()
}
}


ソースコードを一部変更しました。


ちょっと解説

バーコードを読み取って、そこからISBNを取得し、Amazonの商品ページに飛ばしてます。

会社の全社会でお酒を飲みながら作ったので、convartISBN(_: String)の部分でfor文使っているところはご愛嬌でお願いします...。

というかその時、違うループを思い浮かばなかったです...。

最初に苦労したのはObjective-Cで使うlong longInt64で表現しているところですかね。


終わりに

全社会での発表でこれ作ったらもっと楽にできるのになって思いついて、30分ほどで作れました。

みんなからは技術ってすごいんですねーって感動してもらえました。

エンジニアになってよかったなって思う瞬間でした。

以上です。