128
143

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-06-05

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分ほどで作れました。

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

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

以上です。

128
143
1

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
128
143

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?