做者:AppCoda,原文連接,原文日期:2016-05-19
譯者:Prayer;校對:numbbbbb;定稿:CMBios
排着長隊等待結帳的商店,幫助旅客記錄包裹和航班信息的機場,幫助大型零售商處理大量無聊的存貨清單,這些場景很是適合使用條碼掃描器。此外,條碼掃描器也能幫消費者進行智能購物和產品分類。既然它這麼棒,不如咱們在 iPhone 上作一個吧!git
幸運的是,對 Apple 開發者來講,實現條碼掃描很是容易,蘋果大法好!咱們會使用 AV Foundation 來實現一個小巧的 iPhone app,可以掃描 CD 上的條碼,獲取專輯的一些重要信息,並將內容輸出到 App 視圖中。可以實現讀取條碼的功能,這很是的酷,可是咱們的野心不止於此,咱們會對識別的條碼內容做進一步的操做。github
我本不應再多囉嗦,不過仍是友情提醒一下,這個條碼掃描 app 只有在設備具備攝像頭時才能正確工做。記住這一點,準備一臺有攝像頭的 iOS 設備,咱們開始吧!數據庫
今天咱們建立的應用叫作 CDBarcodes —— 它仍是很智能的。當設備掃描到一個條碼時,咱們會將處理後的條碼內容發送給 Discogs 數據庫,而後得到專輯的名稱、藝術家以及發佈年份。Discogs 的數據庫中有大量的音樂數據,因此咱們基本上能查到全部數據。json
從這裏下載 CDBarcodes 的 starter projectswift
先從 Discogs 開始。首先,咱們須要登陸或者註冊一個 Discogs 帳戶。登陸以後,拉到網站的最底端,在 footer 的最左邊邊欄,點擊 API。api
在 Discogs API 頁面,點擊左邊欄 Database 中的 Search。數組
這個就是咱們將會用到的 API。咱們使用 「title」 和 「year」 參數來獲取專輯信息。xcode
如今咱們須要將查詢的 URL 保存到咱們的 CDBarcodes 中。在 Constants.swift
文件中,將 https://api.discogs.com/database/search?q=
添加到常量 DISCOGS_AUTH_URL
中。ruby
let DISCOGS_AUTH_URL = "https://api.discogs.com/database/search?q="
如今咱們能夠很方便地在應用中使用 DISCOGS_AUTH_URL
獲取查詢 URL。
回到剛纔的 Discogs API 網站。咱們須要建立一個新應用,取得 API 的使用資格。在導航欄中,網頁的最頂部,點擊 Create an App。以後點擊 Create an Application 按鈕。
應用名稱的話,輸入 「CDBarcodes + 你的名字」,或者其餘你喜歡的名字。description 字段能夠寫:
「This is an iOS app that reads barcodes from CDs and displays information about the albums.」
譯註:「這個 iOS 應用會讀取 CD 的條形碼並顯示唱片信息。」
最後,點擊 Create Application 按鈕。
在最後的結果頁面,咱們可以獲得使用條碼來作一些操做的資格信息。
拷貝 Consumer Key,粘貼到 Constants.swift
文件的 DISCOGS_KEY
中。再拷貝 Consumer Secret,粘貼到 Constants.swift
文件的 DISCOGS_SECRET
中。
同 URL 同樣,如今咱們能夠在應用中很方便地使用這些變量了。
爲了可以和 Discogs API 通訊,咱們使用一個優秀的第三方庫管理工具:CocoaPods。若是想要了解更多關於 CocoaPods 的信息,或者想學習如何安裝它,能夠到它的官網查詢。
有了 CocoaPods 就能夠安裝第三方庫,咱們會使用 Alamofire 來請求網絡,使用 SwiftyJSON 來處理從 Discogs 返回的 JSON 數據。
下面咱們把這兩個庫引入到 CDBarcodes 工程中!
CocoaPods 安裝好以後,打開終端,進入 CDBarcodes 目錄,初始化 CocoaPods,命令以下:
bash cd <your-xcode-project-directory> pod init
使用 Xcode 打開 Podfile:
bash open -a Xcode Podfile
將下面內容拷貝到 Podfile 中:
ruby source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' use_frameworks! pod 'Alamofire', '~> 3.0' target ‘CDBarcodes’ do pod 'SwiftyJSON', :git => 'https://github.com/SwiftyJSON/SwiftyJSON.git' end
最後,使用下面的命令來下載 Alamofire 和 SwiftyJSON:
bash pod install
如今讓咱們回到 Xcode 中!切記要打開的是 CDBarcodes.xcworkspace
AV Foundation 框架提供了識別條碼的工具。咱們來大概描述一下工做原理。
AVCaptureSession 會管理從攝像頭獲取的數據——將輸入的數據轉爲可使用的輸出
AVCaptureDevice 表示物理設備和其餘屬性。AVCaptureSession 會從 AVCaptureDevice 獲取輸入數據
AVCaptureDeviceInput 從設備中捕獲數據
AVCaptureMetadataOutput 會向處理數據的 delegate 轉發得到的元數據
在 BarcodeReaderViewController.swift
文件中,首先導入 AVFoundation
import UIKit import AVFoundation
同時,咱們須要遵循 AVCaptureMetadataOutputObjectsDelegate
協議。
在 viewDidLoad()
中,咱們要發動條碼掃描引擎。
首先,建立一個 AVCaptureSession
對象,而後設置 AVCaptureDevice
。以後咱們將建立一個輸入對象(input object),而後將其加入到 AVCaptureSession
中。
class BarcodeReaderViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate { var session: AVCaptureSession! var previewLayer: AVCaptureVideoPreviewLayer! override func viewDidLoad() { super.viewDidLoad() // 建立一個 session 對象 session = AVCaptureSession() // 設置 captureDevice. let videoCaptureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo) // 建立 input object. let videoInput: AVCaptureDeviceInput? do { videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice) } catch { return } // 將 input 加入到 session 中 if (session.canAddInput(videoInput)) { session.addInput(videoInput) } else { scanningNotPossible() }
若是你的設備沒有攝像頭,那就沒法掃描條碼。咱們添加了一個處理失敗場景的方法。若是沒有攝像頭,會彈出一個提示框來提示用戶,換一個有攝像頭的設備來掃描 CD 的條碼。
func scanningNotPossible() { // 告知用戶該設備沒法進行條碼掃描 let alert = UIAlertController(title: "Can't Scan.", message: "Let's try a device equipped with a camera.", preferredStyle: .Alert) alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil)) presentViewController(alert, animated: true, completion: nil) session = nil }
回到 viewDidLoad()
方法中,將 input 添加到 session 以後,咱們須要建立 AVCaptureMetadataOutput
並把它也添加到 session 中。咱們會將捕獲到的數據經過串行隊列發送給 delegate 對象。
下一步須要聲明咱們將要掃描的條碼類型。對咱們而言,咱們須要使用 EAN-13 條碼。有意思的是,咱們掃描的條碼並不是都是 EAN-13 類型的;一些有多是 UPC-A 類型,這可能會形成識別的問題。
Apple 經過在前面加上 0 來將 UPC-A 條碼轉換爲 EAN-13 條碼。UPC-A 條碼只有 12 位,EAN-13 條碼,和你猜想的同樣,是 13 位。這個自動轉化特性的好處是,咱們在設置 metadataObjectTypes
時,只要設置爲 AVMetadataObjectTypeEAN13Code
,EAN-13 和 UPC-A 條碼都將會被識別。不過這會修改條碼,所以有可能會在查詢 Discogs 時出問題,後面咱們會處理這個問題。
若是攝像頭有問題,咱們須要使用 scanningNotPossible()
來告知用戶。
// 建立 output 對象 let metadataOutput = AVCaptureMetadataOutput() // 將 output 對象添加到 session 上 if (session.canAddOutput(metadataOutput)) { session.addOutput(metadataOutput) // 經過串行隊列,將捕獲到的數據發送給相應的代理 metadataOutput.setMetadataObjectsDelegate(self, queue: dispatch_get_main_queue()) // 設置可掃描的條碼類型 metadataOutput.metadataObjectTypes = [AVMetadataObjectTypeEAN13Code] } else { scanningNotPossible() }
咱們已經擁有了掃描條碼的強大能力,如今須要作的是預覽掃描畫面。使用 AVCaptureVideoPreviewLayer
在整個屏幕上顯示拍攝到的畫面。
而後,咱們就能夠開始掃描了。
// 添加 previewLayer 讓其顯示攝像頭拍到的畫面 previewLayer = AVCaptureVideoPreviewLayer(session: session); previewLayer.frame = view.layer.bounds; previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; view.layer.addSublayer(previewLayer); // 開始運行 session session.startRunning()
在 captureOutput:didOutputMetadataObjects:fromConnection
方法中,咱們能夠慶祝一下,由於執行到該方法就說明已經識別了一些信息。
首先,咱們須要從 metadataObjects
數組中取出第一個對象,而後將其轉化爲機器能夠識別的格式。而後將轉換後的 readableCode
做爲一個 string 值傳入 barcodeDetected()
方法中。
在看 barcodeDetected()
方法以前,咱們須要以震動的形式給用戶一些掃描成功的反饋而且關閉 session(stop the session)。萬一你忘記關閉了 session,不要緊,你的設備會一直震動,直到你關閉爲止。
func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!) { // 從 metadataObjects 數組中取得第一個對象 if let barcodeData = metadataObjects.first { // 將其轉化爲機器能夠識別的格式 let barcodeReadable = barcodeData as? AVMetadataMachineReadableCodeObject; if let readableCode = barcodeReadable { // 將 readableCode 做爲一個 string 值,傳入 barcodeDetected() 方法中 barcodeDetected(readableCode.stringValue); } // 以震動的形式告知用戶,識別成功 AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate)) // 關閉 session (避免你的設備一直嗡嗡震動) session.stopRunning() } }
咱們須要在 barcodeDetected()
中作一些操做。第一個任務是彈出一個提示框告知用戶,咱們掃描到了一個條碼。而後將掃描到的信息轉化爲咱們須要的內容。
必須去掉掃描內容中的空格。去掉空格以後,咱們須要判斷條碼是 EAN-13 仍是 UPC-A 類型。若是是 EAN-13 類型,不須要額外的操做。若是是 UPC-A 條碼,它被轉化爲了 EAN-13 類型,咱們須要把它還原成原有的格式。
就像咱們以前討論的那樣,蘋果在 UPC-A 條碼的前頭加上一個 0 來將其轉換爲 EAN-13,因此咱們須要判斷其是否以 0 開頭,若是是的話,刪掉它。若是沒有這一步,Discogs 沒法識別這個數字,咱們也沒有辦法獲得正確的數據。
拿處處理後的條碼數據以後,咱們將它傳給 DataService.searchAPI()
而後顯示 BarcodeReaderViewController
func barcodeDetected(code: String) { // 讓用戶知道,咱們掃描到了 let alert = UIAlertController(title: "Found a Barcode!", message: code, preferredStyle: UIAlertControllerStyle.Alert) alert.addAction(UIAlertAction(title: "Search", style: UIAlertActionStyle.Destructive, handler: { action in // 去除空格 let trimmedCode = code.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()) // 判斷是 EAN 仍是 UPC? let trimmedCodeString = "\(trimmedCode)" var trimmedCodeNoZero: String if trimmedCodeString.hasPrefix("0") && trimmedCodeString.characters.count > 1 { trimmedCodeNoZero = String(trimmedCodeString.characters.dropFirst()) // Send the doctored UPC to DataService.searchAPI() DataService.searchAPI(trimmedCodeNoZero) } else { // Send the doctored EAN to DataService.searchAPI() DataService.searchAPI(trimmedCodeString) } self.navigationController?.popViewControllerAnimated(true) })) self.presentViewController(alert, animated: true, completion: nil) }
查看 BarcodeReaderViewController.swift
以前,咱們在 viewDidLoad()
後面添加 viewWillAppear()
和 viewWillDisappear()
。在 viewWillAppear()
方法中,咱們讓 session 開始運行。相應的,在 viewWillDisappear()
方法中,讓 session 中止運行。
override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) if (session?.running == false) { session.startRunning() } } override func viewWillDisappear(animated: Bool) { super.viewWillDisappear(animated) if (session?.running == true) { session.stopRunning() } }
在 DataService.swift
中,咱們將引入 Alamofire 和 SwiftyJSON。
接下來,聲明一些變量來存儲咱們從 Discogs 得到的原始數據。根據 Bionik6 的建議,咱們將使用 private(set)
來實現只讀屬性。
而後建立 Alamofire GET 請求。這裏經過解析 JSON 獲得專輯的名稱和年份。咱們分別把獲得的名稱和年份原始數據賦值給 ALBUM_FROM_DISCOGS
和 YEAR_FROM_DISCOGS
,以後會使用這些變量來建立專輯對象。
如今,咱們從 Discogs 上得到了數據,下面要作的就是展現給全世界!好吧,展現給 AlbumDetailsViewController.swift
就夠了。使用通知的方式來實現。
import Foundation import Alamofire import SwiftyJSON class DataService { static let dataService = DataService() private(set) var ALBUM_FROM_DISCOGS = "" private(set) var YEAR_FROM_DISCOGS = "" static func searchAPI(codeNumber: String) { // 從 Discogs 上獲取專輯數據的 URL let discogsURL = "\(DISCOGS_AUTH_URL)\(codeNumber)&?barcode&key=\(DISCOGS_KEY)&secret=\(DISCOGS_SECRET)" Alamofire.request(.GET, discogsURL) .responseJSON { response in var json = JSON(response.result.value!) let albumArtistTitle = "\(json["results"][0]["title"])" let albumYear = "\(json["results"][0]["year"])" self.dataService.ALBUM_FROM_DISCOGS = albumArtistTitle self.dataService.YEAR_FROM_DISCOGS = albumYear // 發送通知,讓 AlbumDetailsViewController 知道咱們獲得了數據 NSNotificationCenter.defaultCenter().postNotificationName("AlbumNotification", object: nil) } } }
在專輯的數據模型 Album.swift
中,須要將專輯模型轉化爲咱們想要的數據。這個模型接受原始的 artistAlbum
和 albumYear
數據,把它們轉換爲更加易讀的數據。
import Foundation class Album { private(set) var album: String! private(set) var year: String! init(artistAlbum: String, albumYear: String) { // 爲專輯信息添加一些額外的數據 self.album = "Album: \n\(artistAlbum)" self.year = "Released in: \(albumYear)" } }
在 viewDidLoad()
方法中,設置 labels 的內容,提示用戶開始掃描。咱們須要添加 observer 來監聽 NSNotification
從而接收通知。同時須要在 deinit
中移除監聽者。
deinit { NSNotificationCenter.defaultCenter().removeObserver(self) } override func viewDidLoad() { super.viewDidLoad() artistAlbumLabel.text = "Let's scan an album!" yearLabel.text = "" NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(setLabels(_:)), name: "AlbumNotification", object: nil) }
當監聽到通知的時候,setLabels()
方法將會被調用。這裏咱們將使用 DataService.swift
中的原始字符串來初始化 Album
對象。而後將 label 中的內容設置爲咱們想要的 Album
內容。
func setLabels(notification: NSNotification){ // 使用 DataService.swift 中的數據初始化 Album 對象 let albumInfo = Album(artistAlbum: DataService.dataService.ALBUM_FROM_DISCOGS, albumYear: DataService.dataService.YEAR_FROM_DISCOGS) artistAlbumLabel.text = "\(albumInfo.album)" yearLabel.text = "\(albumInfo.year)" }
咱們的 app 完成啦!固然,咱們能夠直接從 CD 封面看到專輯名稱、藝術家和發行年份,可是用咱們的 app 要有趣得多!爲了更好地測試 CDBarcodes 應用,咱們須要找一些 CD 和唱片。這樣就有可能同時遇到 EAN-13 和 UPC-A 條碼,真正發揮 app 的威力。
在 BarcodeReaderViewController
中,注意將相機對焦到條碼上。
這裏是完成以後的 CDBarcodes 代碼。
不管是商務人士、購物者仍是普通人,條碼掃描器都一個特別有用的工具。所以,可以開發條碼掃描也很是有用。
掃描那部分比較有趣。在得到掃描的數據以後,咱們須要對數據作進一步操做,例如判斷是 EAN-13 仍是 UPC-A 類型。咱們須要找到轉化數據的正確方式,而後老司機就上路了。
若是想了解更多內容,能夠讀取其餘的 metadataObjectTypes
和一些新 API。惟一的限制就是你的想象力。
本文由 SwiftGG 翻譯組翻譯,已經得到做者翻譯受權,最新文章請訪問 http://swift.gg。