1.掃碼簡(jiǎn)史
這些年移動(dòng)互聯(lián)網(wǎng)的普及,也讓二維碼技術(shù)成功的推廣。在遙遠(yuǎn)的iOS7.0之前的年代,我們實(shí)現(xiàn)二維碼掃描的功能,還需要借助兩大開(kāi)源組件ZXing和ZBar來(lái)實(shí)現(xiàn)。iOS7.0以后,蘋(píng)果提供了AVFoundation框架,來(lái)實(shí)現(xiàn)二維碼是掃碼,而且效率更高。
與此同時(shí),蘋(píng)果的Swift開(kāi)發(fā)語(yǔ)言,也經(jīng)歷了從1.0誕生到4.1,其中不乏一些新特性以及API的變化。
本文講解了如何用Swift4,實(shí)現(xiàn)二維碼掃描的功能
2.具體實(shí)現(xiàn)
2.1權(quán)限控制
實(shí)現(xiàn)二維碼掃描,必然要打開(kāi)手機(jī)攝像頭,就需要獲取權(quán)限。首先,在你的項(xiàng)目工程的info.plist中加入如下key-value,否則app調(diào)試的時(shí)候崩潰。
<key>NSCameraUsageDescription</key>
<string>CameraUsageDescription</string>
另外需要手動(dòng)去檢測(cè)當(dāng)前APP的攝像頭權(quán)限。如下代碼:
func checkCameraAuth() -> Bool {
let status = AVCaptureDevice.authorizationStatus(for: .video)
return status == .authorized
}
不難看出,status是個(gè)枚舉值,只有 .authorized才是已經(jīng)獲取攝像頭權(quán)限,其余的都不行。
2.2 上代碼
2.2.1 初始化
導(dǎo)入AVFoundation框架之后,我們就可以初始化捕捉設(shè)備、創(chuàng)建捕捉會(huì)話、輸入媒體類(lèi)型、設(shè)置代理等
// 捕捉設(shè)備
guard let device = AVCaptureDevice.default(for: .video) else {
return
}
do {
// 輸入
inPut: AVCaptureDeviceInput = try AVCaptureDeviceInput.init(device: device)
} catch {
print(error)
}
/// 輸出
let outPut: AVCaptureMetadataOutput = {
let outPut = AVCaptureMetadataOutput.init()
outPut.connection(with: .metadata)
return outPut
}()
/// 會(huì)話 session
let session: AVCaptureSession = {
let session = AVCaptureSession.init()
if session.canSetSessionPreset(.high){
session.sessionPreset = .high
}
return session
}()
/// 預(yù)覽層
let preLayer: AVCaptureVideoPreviewLayer = AVCaptureVideoPreviewLayer.init()
2.2.2 設(shè)置代理
初始化之后,開(kāi)始設(shè)置代理
// 設(shè)代理
outPut.setMetadataObjectsDelegate(self as AVCaptureMetadataOutputObjectsDelegate, queue: DispatchQueue.main)
// 指定預(yù)覽層的捕捉會(huì)話
preLayer.session = session
2.2.3 指定會(huì)話輸入輸出
然后把捕捉會(huì)話添加輸入輸出
// 捕捉會(huì)話加入input和output
if session.canAddInput(input) && session.canAddOutput(outPut) {
session.addInput(input)
session.addOutput(outPut)
// 設(shè)置元數(shù)據(jù)處理類(lèi)型(注意, 一定要將設(shè)置元數(shù)據(jù)處理類(lèi)型的代碼添加到 會(huì)話添加輸出之后)
outPut.metadataObjectTypes = [.ean13, .ean8, .upce, .code39, .code93, .code128, .code39Mod43, .qr]
}
設(shè)置元數(shù)據(jù)處理類(lèi)型, 可見(jiàn)不僅有二維碼,而且還有其他條碼,就不一一介紹了。注意, 一定要將設(shè)置元數(shù)據(jù)處理類(lèi)型的代碼添加到會(huì)話添加輸出之后。
2.2.4 添加會(huì)話預(yù)覽圖層
接著開(kāi)始在頁(yè)面添加預(yù)覽層, 這樣才能看到攝像頭捕捉到的畫(huà)面。
// 添加預(yù)覽圖層
let flag = view.layer.sublayers?.contains(preLayer)
if flag == false || flag == nil {
self.preLayer.frame = view.bounds
view.layer.insertSublayer(preLayer, at: 0)
}
2.2.5 開(kāi)啟會(huì)話
到此為止,這個(gè)session捕捉會(huì)話需要的參數(shù)都全了,然后開(kāi)始愉快的開(kāi)始這個(gè)會(huì)話
// 啟動(dòng)會(huì)話
session.startRunning()
2.2.6 監(jiān)聽(tīng)捕捉會(huì)話輸出代理
開(kāi)啟捕捉會(huì)話,我們就可以在代理方法中查看會(huì)話捕捉到的東西。
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!)
識(shí)別到的就在這個(gè)方法里告訴你。
什么?什么?,方法不調(diào)用?
敲黑板!?。PI有變化了
Swift4.0的代理方法在下面
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection){
var resultStrs = [String]()
for obj in metadataObjects {
guard let codeObj = obj as? AVMetadataMachineReadableCodeObject else {
return
}
resultStrs.append(codeObj.stringValue ?? "")
}
}
在這個(gè)方法里面就能拿到掃碼之后的結(jié)果了。
2.2.7 思考
寫(xiě)到這里,不經(jīng)停下思考:
1.這里還只是基本的掃碼功能,就已經(jīng)這么多代碼了,關(guān)于掃碼頁(yè)面的長(zhǎng)啥樣子的代碼我還沒(méi)寫(xiě);
2.一般開(kāi)發(fā)中頁(yè)面少不了UI的網(wǎng)絡(luò)的代碼,難道我要再把這么一大坨AVFoundation代碼都寫(xiě)到控制器嗎?
3.如果我一個(gè)項(xiàng)目里面,不止一個(gè)地方用到掃碼,難道我還要再把這么多代碼再?gòu)?fù)制幾遍

3.封裝
高內(nèi)聚 低耦合
就按照這個(gè)原則來(lái)封裝。
1.首先把這些AVFoundation模塊的代碼,統(tǒng)統(tǒng)抽到一個(gè)工具類(lèi)里,需要的時(shí)候,直接拿工具類(lèi)調(diào)用,識(shí)別結(jié)果delegate返回。
2.可以根據(jù)經(jīng)驗(yàn),把一些定制的需求也放進(jìn)去,比如說(shuō)掃碼的時(shí)候,中間透明的框框,加上周邊的黑色蒙板。
3.擴(kuò)展一些其他功能,比如掃碼成功播放一段提示音等待
什么?你準(zhǔn)備動(dòng)手了?別著急,我已經(jīng)弄好了,使勁戳????
HRQRCodeScanTool
最簡(jiǎn)單的,在控制器中,你只需要
// in ViewController
HRQRCodeScanTool.shared.delegate = self
HRQRCodeScanTool.shared.beginScanInView(view: view)
然后掃碼結(jié)果代理返回
// scan result will call in delegate methods
func scanQRCodeFaild(error: HRQRCodeTooError){
print(error)
}
func scanQRCodeSuccess(resultStrs: [String]){
print(resultStrs.first)
}
如果你需要二維碼描邊,你只需要設(shè)置這幾個(gè)屬性
open var isDrawQRCodeRect: Bool true 是否描繪二維碼邊框 默認(rèn)true
open var drawRectColor: UIColor UIColor.red 二維碼邊框顏色 默認(rèn)紅色
open var drawRectLineWith: CGFloat 2 二維碼邊框線寬 默認(rèn)2
如果你需要添加蒙板,你只需要設(shè)置這幾個(gè)屬性
open var isShowMask: Bool true 是否展示黑色蒙版板層 默認(rèn)開(kāi)啟
open var maskColor: UIColor Black.alpha 0.5 蒙板層 默認(rèn)黑色 alpha 0.5
open var centerWidth: CGFloat 200 中心非蒙板區(qū)域的寬
open var centerHeight: CGFloat 5.0 中心非蒙板區(qū)域的寬
open var centerPosition: CGPoint? nil 中心非蒙板區(qū)域的中心點(diǎn) 默認(rèn)Veiw的中心
哪里需要掃碼,直接接入工具類(lèi),沒(méi)多少行代碼搞定,就問(wèn)你爽不爽。
另外,項(xiàng)目里還提供了兩個(gè)擴(kuò)展,用來(lái)識(shí)別二維碼圖片,以及圖片生成二維碼,需要的各位看官老爺自取。

還支持Cocoapods哦