Swift 之二维码的生成与识别

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)
  
  /// Create bitmap
  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
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值