二维码扫描不工作

QR Code Scanning not working

所以我刚刚接手了一个 iOS 项目作为他们的第一个内部开发人员,之前这个应用程序是由一家机构开发的。

该应用程序的一个特点是它需要扫描二维码——从代码来看,之前的开发者已经按照thisAppCoda上的教程实现了二维码扫描。一切看起来都很好,我看不出代码有任何问题,但它无法正常工作。

我还下载了完整的教程项目,并且在我尝试二维码扫描时有效。我还尝试复制并粘贴每一行,以使其与工作教程相同,但没有成功。

我绞尽脑汁想弄清楚为什么它不起作用。

非常感谢任何帮助!

enum ScanState : Int {
  case newDevice = 1
  case resetDevice = 2
  case replaceDevice = 3
}

class QRScannerViewController: BaseViewController,AVCaptureMetadataOutputObjectsDelegate {
  @IBOutlet var scanZoneView: UIView!
  @IBOutlet var scannerVIew: UIView!
  @IBOutlet var scanInfoLabel: UILabel!

  var capturedQR: String? = nil
  var captureSession:AVCaptureSession?
  var videoPreviewLayer:AVCaptureVideoPreviewLayer?
  var qrCodeFrameView:UIView?
  let supportedBarCodes = [AVMetadataObject.ObjectType.qr, AVMetadataObject.ObjectType.code128, AVMetadataObject.ObjectType.code39, AVMetadataObject.ObjectType.code93, AVMetadataObject.ObjectType.upce, AVMetadataObject.ObjectType.pdf417, AVMetadataObject.ObjectType.ean13, AVMetadataObject.ObjectType.aztec]
  var type = "leg scan"


  var device:Device?
  var state:ScanState = .newDevice

  override func viewDidLoad() {
    super.viewDidLoad()

    scanInfoLabel.text = "Scan your existing\nleg QR code"

    self.navigationController?.navigationBar.dark()

    //self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .Plain, target: nil, action: nil)
  }

  override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    self.navigationController?.setNavigationBarHidden(false, animated: true)
  }

  override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    #if NOHARDWARE
      moveToNextViewController()
    #else
      initiateCapture()
    #endif
  }

  func initiateCapture() {
    let captureDevice = AVCaptureDevice.default(for: AVMediaType.video)
    // Get an instance of the AVCaptureDeviceInput class using the previous device object.
    var error:NSError?
    let input: AnyObject!
    do {
      input = try AVCaptureDeviceInput(device: captureDevice!) as AVCaptureDeviceInput
    } catch let error1 as NSError {
      error = error1
      input = nil
    } catch _ {
      input = nil
    }

    if (error != nil) {
      // If any error occurs, simply log the description of it and don't continue any more.
      print("\(error?.localizedDescription)")
      return
    }
    // Initialize the captureSession object.
    captureSession = AVCaptureSession()
    // Set the input device on the capture session.
    captureSession?.addInput(input as! AVCaptureInput)

    // Initialize a AVCaptureMetadataOutput object and set it as the output device to the capture session.
    let captureMetadataOutput = AVCaptureMetadataOutput()
    captureSession?.addOutput(captureMetadataOutput)

    // Set delegate and use the default dispatch queue to execute the call back
    captureMetadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
    captureMetadataOutput.metadataObjectTypes = supportedBarCodes

    // Initialize the video preview layer and add it as a sublayer to the viewPreview view's layer.
    videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession!)
    videoPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
    videoPreviewLayer?.frame = scannerVIew.layer.bounds
    scannerVIew.layer.addSublayer(videoPreviewLayer!)

    // Start video capture.
    captureSession?.startRunning()


    // Initialize QR Code Frame to highlight the QR code
    qrCodeFrameView = UIView()
    qrCodeFrameView?.layer.borderColor = UIColor.green.cgColor
    qrCodeFrameView?.layer.borderWidth = 2
    scannerVIew.addSubview(qrCodeFrameView!)
    scannerVIew.bringSubview(toFront: qrCodeFrameView!)
    //qrCapturedLabel.text = "No QR code is detected"

  }

  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
  }

  func metadataOutput(captureOutput: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {

    // Check if the metadataObjects array is not nil and it contains at least one object.
    if metadataObjects == nil || metadataObjects.count == 0 {
      qrCodeFrameView?.frame = CGRect.zero

      return
    }

    // Get the metadata object.
    let metadataObj = metadataObjects[0] as! AVMetadataMachineReadableCodeObject

    // Here we use filter method to check if the type of metadataObj is supported
    // Instead of hardcoding the AVMetadataObjectTypeQRCode, we check if the type
    // can be found in the array of supported bar codes.
    if supportedBarCodes.filter({ [=10=] == metadataObj.type }).count > 0 {
      // If the found metadata is equal to the QR code metadata then update the status label's text and set the bounds
      let barCodeObject = videoPreviewLayer?.transformedMetadataObject(for: metadataObj as AVMetadataMachineReadableCodeObject) as! AVMetadataMachineReadableCodeObject

      let intersectionRect = barCodeObject.bounds.intersection(self.scanZoneView.frame)

      if !intersectionRect.isNull &&
        (intersectionRect.size.width * intersectionRect.size.height) > self.scanZoneView.bounds.width * self.scanZoneView.bounds.height / 7 {

        qrCodeFrameView?.frame = barCodeObject.bounds

        if process(metadataObj.stringValue!) {
          captureSession?.stopRunning()
        }
      }
    }
  }

  @IBAction func didTapCancel(_ sender: AnyObject) {
    self.dismiss(animated: true, completion: nil)
  }
}

extension QRScannerViewController {


  func process(_ scanText : String) -> Bool {

    var legCode : String

    let codeComponents = scanText.components(separatedBy: ";")
    if codeComponents.count > 0 {
      legCode = codeComponents[0]
    } else {
      // Invalid number of parameters seperated by a ;
      return false
    }

    // TODO Validate the LEG to LEG-XXXXX
    if legCode.hasPrefix("LEG-") {
      let delta: Int64 = 1 * Int64(NSEC_PER_SEC)
      let time = DispatchTime.now() + Double(delta) / Double(NSEC_PER_SEC)

      DispatchQueue.main.asyncAfter(deadline: time, execute: {

        switch self.state {

        case .resetDevice:

          let realm = try! Realm()
          let deviceList = realm.objects(Device.self)
          let lc = legCode

          self.device = deviceList.filter("legCode = %@", lc).first

          if self.device == nil {

            // TODO Error message: Device not associated with LEG
            let vc = ErrorViewController.createErrorViewController(.DeviceNotFound)
            self.present(vc, animated: true, completion: nil)
            return
          }

          self.moveToNextViewController()

        default:
          self.presentingViewController?.dismiss(animated: true, completion: nil)
        }

      });

      return true
    }

    return false
  }

  func moveToNextViewController() {
    let inspectionStoryboard = UIStoryboard(name: "Impact", bundle: nil)

    if let resetVC = inspectionStoryboard.instantiateViewController(withIdentifier: ImpactDetectionViewController.storyboardID) as? ImpactDetectionViewController {
      resetVC.device = device
      // TODO Pass the impact type across too when the G2 API is set
      self.navigationController?.pushViewController(resetVC, animated: false)
    }
  }

  @IBAction func cancelToVC(_ segue: UIStoryboardSegue) { }

}

编辑

不工作是指 AVCaptureMetadataOutputObjectsDelegate 的委托从未被调用,因此它似乎从未检测到 QR 码。在 AppCoda 教程中,它在检测二维码的地方覆盖了一个绿色方块,但是当我将该代码放入此应用程序时,这种情况从未发生过。

相机实际上是 运行,但从未检测到 QR 码。

您没有将 class 连接到 AVCaptureMetadataOutput 的委托,这就是 AVCaptureMetadataOutputObjectsDelegate 函数未被调用的原因。更多信息:https://developer.apple.com/documentation/avfoundation/avcapturemetadataoutputobjectsdelegate

试试这个:

// Initialize a AVCaptureMetadataOutput object and set it as the output device to the capture session.
    let captureMetadataOutput = AVCaptureMetadataOutput()
    captureMetadataOutput.metadataObjectsDelegate = self // assign self to the delegate.
    captureSession?.addOutput(captureMetadataOutput)

事实证明,答案看似(而且令人讨厌)简单!当然,在 Swift 4 中,代表已稍微重命名。要修复我所要做的就是改变

  func metadataOutput(captureOutput: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {

  func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {