最近在項目中遇到了涉及二維碼相關的問題, 這裏想記錄總結一下二維碼相關技術git
inputMessage
filter?.setValue("H", forKey: "inputCorrectionLevel")
複製代碼
inputCorrectionLevel
是一個單字母(@"L", @"M", @"Q", @"H" 中的一個),表示不一樣級別的容錯率,默認爲 @"M"./* * @param inputMsg 二維碼保存的信息 * @param fgImage 前景圖片 */
func generateCode(inputMsg: String, fgImage: UIImage?) -> UIImage {
//1. 將內容生成二維碼
//1.1 建立濾鏡
let filter = CIFilter(name: "CIQRCodeGenerator")
//1.2 恢復默認設置
filter?.setDefaults()
//1.3 設置生成的二維碼的容錯率
//value = @"L/M/Q/H"
filter?.setValue("H", forKey: "inputCorrectionLevel")
// 2.設置輸入的內容(KVC)
// 注意:key = inputMessage, value必須是NSData類型
let inputData = inputMsg.data(using: .utf8)
filter?.setValue(inputData, forKey: "inputMessage")
//3. 獲取輸出的圖片
guard let outImage = filter?.outputImage else { return UIImage() }
//4. 獲取高清圖片
let hdImage = getHDImage(outImage)
//5. 判斷是否有前景圖片
if fgImage == nil{
return hdImage
}
//6. 獲取有前景圖片的二維碼
return getResultImage(hdImage: hdImage, fgImage: fgImage!)
}
複製代碼
//4. 獲取高清圖片
fileprivate func getHDImage(_ outImage: CIImage) -> UIImage {
let transform = CGAffineTransform(scaleX: 10, y: 10)
//放大圖片
let ciImage = outImage.transformed(by: transform)
return UIImage(ciImage: ciImage)
}
複製代碼
//獲取前景圖片
fileprivate func getResultImage(hdImage: UIImage, fgImage: UIImage) -> UIImage {
let hdSize = hdImage.size
//1. 開啓圖形上下文
UIGraphicsBeginImageContext(hdSize)
//2. 將高清圖片畫到上下文
hdImage.draw(in: CGRect(x: 0, y: 0, width: hdSize.width, height: hdSize.height))
//3. 將前景圖片畫到上下文
let fgWidth: CGFloat = 80
fgImage.draw(in: CGRect(x: (hdSize.width - fgWidth) / 2, y: (hdSize.height - fgWidth) / 2, width: fgWidth, height: fgWidth))
//4. 獲取上下文
guard let resultImage = UIGraphicsGetImageFromCurrentImageContext() else { return UIImage() }
//5. 關閉上下文
UIGraphicsEndImageContext()
return resultImage
}
複製代碼
後續會研究彩色二維碼的黑科技, 敬請期待...github
識別圖片中二維碼步驟數組
/* * @param qrCodeImage 二維碼的圖片 * @return 結果的數組 */
func recognitionQRCode(qrCodeImage: UIImage) -> [String]? {
//1. 建立過濾器
let detector = CIDetector(ofType: CIDetectorTypeQRCode, context: nil, options: nil)
//2. 獲取CIImage
guard let ciImage = CIImage(image: qrCodeImage) else { return nil }
//3. 識別二維碼
guard let features = detector?.features(in: ciImage) else { return nil }
//4. 遍歷數組, 獲取信息
var resultArr = [String]()
for feature in features {
resultArr.append(feature.type)
}
return resultArr
}
複製代碼
//輸入輸出中間橋樑(會話)
fileprivate lazy var session : AVCaptureSession = AVCaptureSession()
複製代碼
AVCaptureMetadataOutputObjectsDelegate
的代理設置, 該協議中的方法會將掃描的結果返回fileprivate func addScaningVideo(){
//1.獲取輸入設備(攝像頭)
guard let device = AVCaptureDevice.default(for: .video) else { return }
//2.根據輸入設備建立輸入對象
guard let deviceInput = try? AVCaptureDeviceInput(device: device) else { return }
//3.建立原數據的輸出對象
let metadataOutput = AVCaptureMetadataOutput()
//4.設置代理監聽輸出對象輸出的數據,在主線程中刷新
metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
//5.建立會話(橋樑)
// let session = AVCaptureSession()
//6.添加輸入和輸出到會話
if session.canAddInput(deviceInput) {
session.addInput(deviceInput)
}
if session.canAddOutput(metadataOutput) {
session.addOutput(metadataOutput)
}
//7.告訴輸出對象要輸出什麼樣的數據(二維碼仍是條形碼),要先建立會話才能設置
metadataOutput.metadataObjectTypes = [.qr, .code128, .code39, .code93, .code39Mod43, .ean8, .ean13, .upce, .pdf417, .aztec]
//8.建立預覽圖層
let previewLayer: AVCaptureVideoPreviewLayer = AVCaptureVideoPreviewLayer(session: session)
previewLayer.videoGravity = .resizeAspectFill
previewLayer.frame = view.bounds
view.layer.insertSublayer(previewLayer, at: 0)
//9.設置有效掃描區域(默認整個屏幕區域)(每一個取值0~1, 以屏幕右上角爲座標原點)
let rect = CGRect(x: scanImageView.frame.minY / kScreenHeight, y: scanImageView.frame.minX / kScreenWidth, width: scanImageView.frame.height / kScreenHeight, height: scanImageView.frame.width / kScreenWidth)
metadataOutput.rectOfInterest = rect
//10. 開始掃描
session.startRunning()
}
複製代碼
extension ScaningViewController: AVCaptureMetadataOutputObjectsDelegate {
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
//1. 取出掃描到的數據: metadataObjects
//2. 以震動的形式告知用戶掃描成功
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
//3. 關閉session
session.stopRunning()
//4. 遍歷結果
var resultArr = [String]()
for result in metadataObjects {
//轉換成機器可讀的編碼數據
if let code = result as? AVMetadataMachineReadableCodeObject {
resultArr.append(code.stringValue ?? "")
}else {
resultArr.append(result.type.rawValue)
}
}
//5. 將結果
let vc = ShowViewController()
vc.scanDataArr = resultArr
navigationController?.pushViewController(vc, animated: true)
}
}
複製代碼