# ****掃描二維碼
## ****第三方框架
ZXing
Android使用多ZBar
iOS使用多提示:以上兩個(gè)框架都是老牌二維碼框架,不過(guò)都不支持 64 位
目前在 iOS 開(kāi)發(fā)中普遍使用蘋果的
AVFoundation
框架,但是不支持圖片識(shí)別功能AVFoundation
只支持通過(guò)攝像頭掃描識(shí)別
## ****識(shí)別原理
g)
## ****代碼實(shí)現(xiàn)
- 拍攝會(huì)話
/// 拍攝會(huì)話握截,是掃描的橋梁
lazy var session: AVCaptureSession = {
return AVCaptureSession()
}()
- 攝像頭輸入設(shè)備
// 2. 輸入設(shè)備
lazy var inputDevice: AVCaptureDeviceInput? = {
let device = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
do {
return try AVCaptureDeviceInput(device: device)
} catch {
print(error)
return nil
}
}()
- 數(shù)據(jù)輸出
/// 數(shù)據(jù)輸出
lazy var dataOutput: AVCaptureMetadataOutput = {
return AVCaptureMetadataOutput()
}()
- 建立通道倚舀,設(shè)置會(huì)話
private func setupSession() {
// 1. 判斷是否能夠添加設(shè)備
if !session.canAddInput(inputDevice) {
print("無(wú)法添加輸入設(shè)備")
return
}
// 2. 判斷能否添加輸出數(shù)據(jù)
if !session.canAddOutput(outputData) {
print("無(wú)法添加輸出數(shù)據(jù)")
return
}
// 3. 添加設(shè)備
session.addInput(inputDevice)
session.addOutput(outputData)
print(outputData.availableMetadataObjectTypes)
// 4. 設(shè)置掃描數(shù)據(jù)類型
outputData.metadataObjectTypes = outputData.availableMetadataObjectTypes
// 5. 設(shè)置輸出代理
outputData.setMetadataObjectsDelegate(self, queue: dispatch_get_main_queue())
}
注意抱虐,一定要把輸出設(shè)備添加到會(huì)話后当凡,才有可用數(shù)據(jù)類型
- 實(shí)現(xiàn)協(xié)議方法
// MARK: - AVCaptureMetadataOutputObjectsDelegate
func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!) {
println(metadataObjects)
}
必須要啟動(dòng)會(huì)話著觉,才能開(kāi)始掃描
/// 開(kāi)始掃描
private func startScan() {
session.startRunning()
}
- 添加預(yù)覽視圖
/// 預(yù)覽圖層
lazy var previewLayer: AVCaptureVideoPreviewLayer = {
return AVCaptureVideoPreviewLayer(session: self.session)
}()
- 設(shè)置圖層
/// 設(shè)置圖層
func setupLayers() {
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
previewLayer.frame = view.bounds
view.layer.insertSublayer(previewLayer, atIndex: 0)
}
進(jìn)一步體會(huì)一下此處的
self.
閉包內(nèi)部需要使用
self.
在
OC
中使用self.
能夠調(diào)用屬性的getter
方法逞刷,確保對(duì)象一定能夠拿到修改掃描代理方法嘉涌,提取數(shù)值
// MARK: - 掃描數(shù)據(jù)代理
func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!) {
for object in metadataObjects {
print(object)
}
}
# ****繪制線條
## AVMetadataMachineReadableCodeObject
- bounds
- corners
## ****代碼實(shí)現(xiàn)
- 坐標(biāo)轉(zhuǎn)換
// MARK: - 掃描數(shù)據(jù)代理
func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!) {
for object in metadataObjects {
let dataObject = previewLayer.transformedMetadataObjectForMetadataObject(object as! AVMetadataObject) as! AVMetadataMachineReadableCodeObject
print(dataObject)
}
}
- 轉(zhuǎn)換結(jié)果
# 轉(zhuǎn)換前
<AVMetadataMachineReadableCodeObject: 0x170220720,
type="org.iso.QRCode",
bounds={ 0.4,0.4 0.1x0.2 }>
corners { 0.4,0.6 0.5,0.6 0.5,0.4 0.4,0.4 },
time 155921691680958,
stringValue "http://weibo.cn/qr/userinfo?uid=5365823342"
# 轉(zhuǎn)換后
<AVMetadataMachineReadableCodeObject: 0x170622cc0,
type="org.iso.QRCode",
bounds={ 116.6,224.9 79.5x80.0 }>
corners { 116.6,226.1 117.2,304.4 196.1,304.9 195.7,224.9 },
time 155921691680958,
stringValue "http://weibo.cn/qr/userinfo?uid=5365823342"
轉(zhuǎn)換的目的是將采集到的坐標(biāo)轉(zhuǎn)換成能夠識(shí)別的坐標(biāo)數(shù)值
- 繪制圖層
/// 繪制圖層
lazy var drawLayer: CALayer = {
return CALayer()
}()
- 添加圖層
/// 設(shè)置圖層
func setupLayers() {
drawLayer.frame = view.bounds
view.layer.insertSublayer(drawLayer, atIndex: 0)
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
previewLayer.frame = view.bounds
view.layer.insertSublayer(previewLayer, atIndex: 0)
}
注意:一定要用
insertSublayer
,否則會(huì)遮擋住TabBar
- 創(chuàng)建路徑 & 繪制條碼形狀
/// 繪制條碼形狀
private func drawCornersShape(dataObject: AVMetadataMachineReadableCodeObject) {
// 判斷數(shù)組是否為空
if dataObject.corners.isEmpty {
return
}
let layer = CAShapeLayer()
layer.lineWidth = 4
layer.strokeColor = UIColor.greenColor().CGColor
layer.fillColor = UIColor.clearColor().CGColor
layer.path = cornersPath(dataObject.corners)
// 添加到繪圖圖層
drawLayer.addSublayer(layer)
}
/// 創(chuàng)建邊線路徑
///
/// -parameter corners: 邊角頂點(diǎn)數(shù)組
private func cornersPath(corners: NSArray) -> CGPathRef {
let path = UIBezierPath()
var point = CGPoint()
// 1. 移動(dòng)到第一個(gè)點(diǎn)
var index = 0
CGPointMakeWithDictionaryRepresentation((corners[index++] as! CFDictionaryRef), &point)
path.moveToPoint(point)
// 2. 遍歷剩余的點(diǎn)
while index < corners.count {
CGPointMakeWithDictionaryRepresentation((corners[index++] as! CFDictionaryRef), &point)
path.addLineToPoint(point)
}
// 3. 關(guān)閉路徑
path.closePath()
return path.CGPath
}
注意
corners
是保存CFDictionary
對(duì)象的數(shù)組一定要判斷
corners
是否包含數(shù)據(jù)夸浅,否則會(huì)崩潰清空繪圖圖層
/// 清空繪圖圖層
private func clearDrawLayer() {
if drawLayer.sublayers == nil {
return
}
for layer in drawLayer.sublayers! {
layer.removeFromSuperlayer()
}
}
注意:一定要判斷
subLayers
否則會(huì)崩潰
- 調(diào)整后的代碼
func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!) {
clearDrawLayer()
for object in metadataObjects {
if object is AVMetadataMachineReadableCodeObject {
let dataObject = previewLayer.transformedMetadataObjectForMetadataObject(object as! AVMetadataObject) as! AVMetadataMachineReadableCodeObject
drawCornersShape(dataObject)
print(dataObject.stringValue)
}
}
}
一定要判斷一下 object 的類型仑最,否則遇到非
CodeObject
會(huì)直接崩潰
# ****生成二維碼
/// 生成二維碼
private func generateQRCodeImage() -> UIImage {
// 1. 生成二維碼
let qrFilter = CIFilter(name: "CIQRCodeGenerator")!
qrFilter.setDefaults()
qrFilter.setValue("任玉飛".dataUsingEncoding(NSUTF8StringEncoding), forKey: "inputMessage")
let ciImage = qrFilter.outputImage
// 2. 縮放處理
let transform = CGAffineTransformMakeScale(10, 10)
let transformImage = ciImage.imageByApplyingTransform(transform)
// 3. 顏色濾鏡
let colorFilter = CIFilter(name: "CIFalseColor")!
colorFilter.setDefaults()
colorFilter.setValue(transformImage, forKey: "inputImage")
// 前景色
colorFilter.setValue(CIColor(color: UIColor.blackColor()), forKey: "inputColor0")
// 背景色
colorFilter.setValue(CIColor(color: UIColor.whiteColor()), forKey: "inputColor1")
let outputImage = colorFilter.outputImage
return insertAvatarImage(UIImage(CIImage: outputImage), avatar: UIImage(named: "avatar")!)
}
- 插入頭像
func insertAvatarImage(qrimage: UIImage, avatar: UIImage) -> UIImage {
UIGraphicsBeginImageContext(qrimage.size)
let rect = CGRect(origin: CGPointZero, size: qrimage.size)
qrimage.drawInRect(rect)
let w = rect.width * 0.2
let x = (rect.width - w) * 0.5
let y = (rect.height - w) * 0.5
avatar.drawInRect(CGRect(x: x, y: y, width: w, height: w))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
查閱濾鏡
print(CIFilter.filterNamesInCategory(kCICategoryBuiltIn))