序言
在iOS7之前二維碼掃描主要是用的第三方庫巧还,如ZXing或者ZBar。使用起來比較麻煩坊秸,出錯也不容易調(diào)試麸祷。iOS7之后,蘋果自身提供了二維碼的掃描褒搔、生成功能阶牍,從效率上來說,原生的二維碼遠高于這些第三方框架星瘾。這里主要分享一下swift版二維碼的掃描走孽、生成以及識別圖片中的二維碼。這里是一個完整的demo琳状。
二維碼生成描述
主要用到CIFilter
類磕瓷。CIFilter
是Core Image中一個比較核心的有關(guān)濾鏡使用的類。
通常CIFilter
對象需要一個或多個圖像作為輸入念逞,并產(chǎn)生CIImage
類型的實體作為輸出困食。而這些輸出圖像的生產(chǎn)過程需要我們通過設置一些參數(shù)來實現(xiàn),而這些參數(shù)的設置和檢索都是利用鍵/值對的形式進行操作的翎承。
其中CIFlilter
承載著所有設置好的濾鏡參數(shù)以CIImage
為基礎硕盹,在CIContext
對象中進行渲染。要提一下的是濾鏡的使用是可以疊加的叨咖,我們可以使用多種濾鏡處理同一個圖像瘩例。Core Image
的渲染分為CPU和GPU兩種啊胶,其中使用CPU渲染可以在后臺進行,但是渲染速度沒有GPU快垛贤,而GPU是不能進行后臺渲染的它是實時的焰坪,在進行視頻幀渲染時我們就可以使用GPU進行渲染。
生成二維碼代碼
private func createQRCodeImage(content:String,size:CGSize)->UIImage {
let stringData = content.data(using: String.Encoding.utf8)
let qrFilter = CIFilter(name: "CIQRCodeGenerator")
qrFilter?.setValue(stringData, forKey: "inputMessage")
qrFilter?.setValue("H", forKey: "inputCorrectionLevel")
let colorFilter = CIFilter(name: "CIFalseColor")
colorFilter?.setDefaults()
colorFilter?.setValuesForKeys(["inputImage" : (qrFilter?.outputImage)!,"inputColor0":CIColor.init(cgColor: UIColor.black.cgColor),"inputColor1":CIColor.init(cgColor: UIColor.white.cgColor)])
let qrImage = colorFilter?.outputImage
let cgImage = CIContext(options: nil).createCGImage(qrImage!, from: (qrImage?.extent)!)
UIGraphicsBeginImageContext(size)
let context = UIGraphicsGetCurrentContext()
context!.interpolationQuality = .none
context!.scaleBy(x: 1.0, y: -1.0)
context?.draw(cgImage!, in: (context?.boundingBoxOfClipPath)!)
let codeImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return codeImage!
}
往往實際應用中不會是單獨的二維碼南吮,還需要向二維碼中添加自己的logo琳彩。二維碼有很好的糾錯機制,我們可以直接向二維碼圖片中加入logo圖片部凑,建議logo面積不要超過二維碼有效面積的1/20露乏,否則會影響讀碼。
private func addIconToQRCodeImage(image:UIImage,icon:UIImage,iconSize:CGSize)->UIImage{
UIGraphicsBeginImageContext(image.size)
let imageWidth = image.size.width
let imageHeight = image.size.height
let iconWidth = iconSize.width
let iconHeight = iconSize.height
image.draw(in: CGRect(x: 0, y: 0, width: imageWidth, height: imageHeight))
icon.draw(in: CGRect(x: (imageWidth-iconWidth)/2.0, y: (imageHeight-iconHeight)/2.0, width: iconWidth, height: iconHeight))
let qrImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return qrImage!
}
二維碼掃描
二維碼掃描主要用AVFoundation
涂邀。AVFoundation
是一個很大基礎庫瘟仿,用來創(chuàng)建基于時間的視聽媒體,可以使用它來檢查,創(chuàng)建比勉、編輯或媒體文件劳较。也可以輸入流從設備和操作視頻實時捕捉和回放。
主要成員介紹:
AVCaptureSession
管理輸入(AVCaptureInput
)和輸出(AVCaptureOutput
)流浩聋,包含開啟和停止會話方法观蜗。
AVCaptureDeviceInput
是AVCaptureInput
的子類,可以作為輸入捕獲會話,用AVCaptureDevice
實例初始化衣洁。
AVCaptureDevice
代表了物理捕獲設備如:攝像機墓捻。用于配置等底層硬件設置相機的自動對焦模式。
AVCaptureMetadataOutput
是AVCaptureOutput
的子類坊夫,處理輸出捕獲會話砖第。捕獲的對象傳遞給一個委托實現(xiàn)AVCaptureMetadataOutputObjectsDelegate
協(xié)議。協(xié)議方法在指定的派發(fā)隊列(dispatch queue
)上執(zhí)行环凿。
AVCaptureVideoPreviewLayerCALayer
的一個子類梧兼,顯示捕獲到的相機輸出流。
import AVFoundation
let session = AVCaptureSession()
var layer: AVCaptureVideoPreviewLayer?```
創(chuàng)建會話智听,讀取流:
private func scanQRCode(){
if !isCameraAvailable(){
showAlert("請使用真機", message: "您的設備沒有相機.", VC: self)
return
}
self.session.sessionPreset = AVCaptureSessionPresetHigh
do{
let input = try AVCaptureDeviceInput(device: self.device)
if self.session.canAddInput(input){
self.session.addInput(input)
}
}
catch{
showAlert("錯誤", message: "初始化失敗", VC: self)
return
}
self.layer = AVCaptureVideoPreviewLayer(session: self.session)
self.layer?.videoGravity = AVLayerVideoGravityResizeAspectFill
self.layer?.frame = self.view.frame
self.view.layer.addSublayer(self.layer!)
self.session.addObserver(self, forKeyPath: "running", options: .new, context: nil)
if AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo) == .authorized{
let output = AVCaptureMetadataOutput()
output.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
//設置掃描的有效區(qū)域
output.rectOfInterest = CGRect(x: 140.0/SCREEN_HEIGHT, y: 40.0/SCREEN_WIDTH, width: (SCREEN_WIDTH-80)/SCREEN_HEIGHT, height: (SCREEN_WIDTH-80)/SCREEN_WIDTH)
if self.session.canAddOutput(output) {
self.session.addOutput(output)
output.metadataObjectTypes = [AVMetadataObjectTypeQRCode]
}
self.setOverlayPickerView()
self.session.startRunning()
}else{
showAlert("提示", message: "Authorization is required to use the camera, please check your permission settings: Settings> Privacy> Camera", VC: self)
}
}
獲取捕獲數(shù)據(jù):
//二維碼掃描代理
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
var stringValue:String?
if metadataObjects.count > 0 {
let metadataObject = metadataObjects[0] as! AVMetadataMachineReadableCodeObject
stringValue = metadataObject.stringValue
}
self.session.stopRunning()
print("code is \(stringValue)")
let result:JCQRResultViewController = JCQRResultViewController()
result.urlString = stringValue!
self.navigationController?.pushViewController(result, animated: true)
}
> 需要注意的是```rectOfInterest``` 這個屬性的每一個值取值范圍在0~1之間羽杰,代表的是對應軸上的比例大小。
![掃一掃.jpeg](http://upload-images.jianshu.io/upload_images/3288439-aa1fbcb29632c52e.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
識別圖片中的二維碼
-------------
識別圖片中的代碼非常簡單到推,用```CIDetector```一句代碼就好了忽洛。
fileprivate func readQRImage(_ image:UIImage)->String{
//二維碼讀取
let ciImage:CIImage=CIImage(image:image)!
let context = CIContext(options: nil)
let detector:CIDetector=CIDetector(ofType: CIDetectorTypeQRCode,
context: context, options: [CIDetectorAccuracy:CIDetectorAccuracyHigh])!
let features=detector.features(in: ciImage)
print("掃描到二維碼個數(shù):(features.count)")
var stringValue:String = ""
if features.count<=0 {
showAlert("提示", message: "無法解析圖片",VC: self)
}else {
//遍歷所有的二維碼,并框出
for feature in features as! [CIQRCodeFeature] {
print(feature.messageString)
stringValue = feature.messageString!
}
}
return stringValue
}