1、二维码的生成
- 参考资料:https://www.jianshu.com/p/a112ff14387f
let filter = CIFilter.init(name: "CIQRCodeGenerator")
filter?.setDefaults()
let data = qrUrl.data(using: .utf8)
filter?.setValue(data, forKeyPath: "inputMessage")
let outputImage = filter?.outputImage
if let outputImage = outputImage {
qrCodeView.image = createUIImageFromCIImage(image: outputImage, size: 100)
func createUIImageFromCIImage(image: CIImage, size: CGFloat) -> UIImage {
let extent = image.extent.integral
let scale = min(size / extent.width, size / extent.height)
let width = size_t(extent.width * scale)
let height = size_t(extent.height * scale)
let cs: CGColorSpace = CGColorSpaceCreateDeviceGray()
let bitmap = CGContext(data: nil, width: width, height: height, bitsPerComponent: 8, bytesPerRow: 0, space: cs, bitmapInfo: 1)
let context = CIContext.init()
let bitmapImage = context.createCGImage(image, from: extent)
if let bitmap = bitmap, let bitmapImage = bitmapImage {
bitmap.interpolationQuality = .none
bitmap.scaleBy(x: scale, y: scale)
bitmap.draw(bitmapImage, in: extent)
if let scaledImage = bitmap.makeImage() {
return UIImage.init(cgImage: scaledImage)
}
}
return UIImage()
}
2、二维码的识别
- 调用相机识别二维码
- 从相册中选取图片识别二维码
- 参考资料:
https://blog.csdn.net/leafgw/article/details/51096568
https://blog.csdn.net/qq_39097425/article/details/88985484
import Foundation
import UIKit
import AVFoundation
class ScanQrCodeViewController: UIViewController {
private var session: AVCaptureSession?
private let scanLine = UIImageView()
private let screenWidth = UIScreen.main.bounds.size.width
private let screenHeight = UIScreen.main.bounds.size.height
private let barcodeView: UIView
private var timer = Timer()
init() {
barcodeView = UIView(frame: CGRect(x: screenWidth * 0.2, y: screenHeight * 0.35, width: screenWidth * 0.6, height: screenWidth * 0.6))
super.init(nibName: nil, bundle: nil)
addBackgroundView()
view.addSubviews(barcodeView, pictureView)
barcodeView.addSubviews(scanLine)
barcodeView.layer.borderWidth = 2.0
barcodeView.layer.borderColor = UIColor.white.cgColor
scanLine.frame = CGRect(x: 0, y: 0, width: barcodeView.frame.size.width, height: 1.0)
scanLine.backgroundColor = UIColor(hex: 0x00FF00)
pictureView.snp.makeConstraints { make in
make.top.equalTo(barcodeView.snp.bottom).offset(40.0)
make.centerX.equalToSuperview()
}
timer = Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(moveScannerLayer(_:)), userInfo: nil, repeats: true)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func addBackgroundView() {
let topView = UIView(frame: CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight * 0.35))
let bottomView = UIView(frame: CGRect(x: 0, y: screenWidth * 0.6 + screenHeight * 0.35, width: screenWidth, height: screenHeight * 0.65 - screenWidth * 0.6))
let leftView = UIView(frame: CGRect(x: 0, y: screenHeight * 0.35, width: screenWidth * 0.2, height: screenWidth * 0.6))
let rightView = UIView(frame: CGRect(x: screenWidth * 0.8, y: screenHeight * 0.35, width: screenWidth * 0.2, height: screenWidth * 0.6))
let color = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4)
topView.backgroundColor = color
bottomView.backgroundColor = color
leftView.backgroundColor = color
rightView.backgroundColor = color
view.addSubviews(topView, bottomView, leftView, rightView)
}
private lazy var pictureView: UIImageView = {
let image = Resource.image.message_visible()
let view = UIImageView(image: image)
view.addOnClickListener(target: self, action: #selector(onFromAlbum))
return view
}()
public override func viewDidLoad() {
super.viewDidLoad()
let device = AVCaptureDevice.default(for: .video)
session = AVCaptureSession()
guard let device = device, let session = session else {
return
}
do {
let input: AVCaptureDeviceInput? = try AVCaptureDeviceInput(device: device)
if let input = input, session.canAddInput(input) {
session.addInput(input)
}
let output = AVCaptureMetadataOutput()
if session.canAddOutput(output) {
session.addOutput(output)
output.setMetadataObjectsDelegate(self, queue: .main)
output.metadataObjectTypes = [AVMetadataObject.ObjectType.qr]
}
} catch {
print("error: \(error)")
}
let layer = AVCaptureVideoPreviewLayer(session: session)
layer.videoGravity = AVLayerVideoGravity.resizeAspectFill
layer.frame = self.view.bounds
self.view.layer.insertSublayer(layer, at: 0)
session.startRunning()
}
}
extension ScanQrCodeViewController: AVCaptureMetadataOutputObjectsDelegate {
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
session?.startRunning()
if let metadataObjects = metadataObjects.first {
let readableObejct = metadataObjects as? AVMetadataMachineReadableCodeObject
Toast.toast(at: view, message: "二维码信息:\(String(describing: readableObejct?.stringValue))")
}
}
}
extension ScanQrCodeViewController {
@objc func moveScannerLayer(_ timer: Timer) {
scanLine.frame = CGRect(x: 0, y: 0, width: self.barcodeView.frame.size.width, height: 1.0)
UIView.animate(withDuration: 2) {
self.scanLine.frame = CGRect(x: self.scanLine.frame.origin.x, y: self.scanLine.frame.origin.y + self.barcodeView.frame.size.height - 1.0, width: self.scanLine.frame.size.width, height: self.scanLine.frame.size.height)
}
}
@objc func onFromAlbum() {
print("onFromAlbum")
if !UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
return
}
let imagePickerVC = UIImagePickerController()
imagePickerVC.delegate = self
present(imagePickerVC, animated: true, completion: nil)
}
}
extension ScanQrCodeViewController: UINavigationControllerDelegate, UIImagePickerControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String: Any]) {
guard let image = info[UIImagePickerControllerOriginalImage] as? UIImage else {
return
}
guard let ciImage = CIImage(image: image) else {
return
}
let detector = CIDetector(ofType: CIDetectorTypeQRCode, context: nil, options: [CIDetectorAccuracy: CIDetectorAccuracyLow])
if let results = detector?.features(in: ciImage) {
for result in results {
let qrCodeResult = result as? CIQRCodeFeature
Toast.toast(at: view, message: "二维码信息:\(String(describing: qrCodeResult?.messageString))")
}
picker.dismiss(animated: true, completion: nil)
}
}
}
3、后续问题的发现
- 问题:
url 内容复杂,生成的二维码截图后 ,无法识别成功 - 原因:
如果目标二维码过大,有几率造成识别失败,需对图片进行缩小: - 解决:
https://www.jianshu.com/p/f4214e941fed
private func imageSizeWithScreenImage(image: UIImage) -> UIImage? {
let imageWidth = image.size.width
let imageHeight = image.size.height
if (imageWidth <= screenWidth && imageHeight <= screenHeight) {
return image;
}
let max = max(imageWidth, imageHeight)
let scale = max / (screenHeight * 3.0);
let size = CGSize(width: imageWidth / scale, height: imageHeight / scale)
UIGraphicsBeginImageContext(size)
image.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}