【问题标题】:Camera Barcode Scanning Works on iPhone, But Not iPad相机条码扫描适用于 iPhone,但不适用于 iPad
【发布时间】:2021-10-18 23:05:10
【问题描述】:

我正在使用 AVCaptureDevice API 扫描条码,它在 iPhone 上运行良好,但非常相似的代码在 iPad 上无法运行,我不太清楚为什么(根本没有检测到任何条码)。主要区别在于扫描区域的大小、位置和方向。我使用 iPhone 12 mini(iOS 15 测试版)和原始 iPad Pro 9.7"(iOS 14.6)进行了测试。不确定这是否重要。

以下是扫描仪的代码。如果您发现需要更改的内容,请告诉我。

import Foundation
import AVFoundation
import UIKit

class ScannerViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
    var captureSession: AVCaptureSession!
    var previewLayer: AVCaptureVideoPreviewLayer!
    var barcodeCallback: (String) -> Void
    var cameraScanDismissedCallback: (Bool) -> Void
    var scanned = false
    var currentDevice: AVCaptureDevice!
    var scanRectView: UIView!

    init(barcodeCallback: @escaping (String) -> Void, cameraScanDismissedCallback: @escaping (Bool) -> Void) {
        self.barcodeCallback = barcodeCallback;
        self.cameraScanDismissedCallback = cameraScanDismissedCallback;
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = UIColor.black
        captureSession = AVCaptureSession()

        guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else { return }
        let videoInput: AVCaptureDeviceInput

        do {
            videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
        } catch {
            return
        }

        if (captureSession.canAddInput(videoInput)) {
            captureSession.addInput(videoInput)
        } else {
            failed()
            return
        }

        let metadataOutput = AVCaptureMetadataOutput()

        if (captureSession.canAddOutput(metadataOutput)) {
            captureSession.addOutput(metadataOutput)

            metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
            metadataOutput.metadataObjectTypes = [.qr, .ean8, .ean13, .pdf417, .code128, .aztec, .code39, .code39Mod43, .code93, .dataMatrix, .face, .interleaved2of5, .itf14, .upce]
        } else {
            failed()
            return
        }
    
        let windowSize = UIScreen.main.bounds.size
    
        var scanSize: CGSize!;
        var scanRect: CGRect!;
        if(UIDevice.current.userInterfaceIdiom == .pad){
            scanSize = CGSize(width:windowSize.width*1/3, height:windowSize.width*1/7);
            scanRect = CGRect(x: UIScreen.main.bounds.midX - scanSize.width/2,
                          y: UIScreen.main.bounds.midY - scanSize.height/2,
                          width:scanSize.width, height:scanSize.height);
        }else{
            scanSize = CGSize(width:windowSize.width*2/3, height:windowSize.width*1/3);
            scanRect = CGRect(x: UIScreen.main.bounds.midX - scanSize.width/2,
                          y: UIScreen.main.bounds.midY - scanSize.height/2,
                          width:scanSize.width, height:scanSize.height);
        }
    
        scanRect = CGRect(x:scanRect.origin.y/windowSize.height,
                      y:scanRect.origin.x/windowSize.width,
                      width:scanRect.size.height/windowSize.height,
                      height:scanRect.size.width/windowSize.width);
        metadataOutput.rectOfInterest = scanRect

        previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
    
        if(UIDevice.current.userInterfaceIdiom == .pad){
            let orientation: UIDeviceOrientation = UIDevice.current.orientation
            previewLayer.connection?.videoOrientation = {
                switch (orientation) {
                case .faceUp:
                    return .landscapeLeft
                case .portrait:
                    return .portrait
                case .landscapeRight:
                    return .landscapeLeft
                case .landscapeLeft:
                    return .landscapeRight
                default:
                    return .portrait
                }
            }()
        }
    
        previewLayer.frame = view.layer.bounds
        previewLayer.videoGravity = .resizeAspectFill
        view.layer.addSublayer(previewLayer)
    
        scanRectView = UIView();
        view.addSubview(self.scanRectView)
        scanRectView.frame = CGRect(x:0, y:0, width: scanSize.width,
                                     height: scanSize.height);
    
        if(UIDevice.current.userInterfaceIdiom == .pad){
            scanRectView.center = CGPoint( x:UIScreen.main.bounds.midX - scanSize.width/2,
                                            y:UIScreen.main.bounds.midY - scanSize.height/2)
        }else{
            scanRectView.center = CGPoint( x:UIScreen.main.bounds.midX,
                                       y:UIScreen.main.bounds.midY)
        }
    
        scanRectView.layer.borderColor = UIColor.yellow.cgColor
        scanRectView.layer.borderWidth = 5;

        currentDevice = videoCaptureDevice
        captureSession.startRunning()
        toggleTorch(on: true)
    }

    func toggleTorch(on: Bool) {
        guard let device = currentDevice else { return }

        if device.hasTorch {
            do {
                try device.lockForConfiguration()
            
                if(UIDevice.current.userInterfaceIdiom == .pad){
                    device.videoZoomFactor = 1.3
                }else{
                    device.videoZoomFactor = 1.5
                }

                if on == true {
                    device.torchMode = .on
                } else {
                    device.torchMode = .off
                }

                device.unlockForConfiguration()
            } catch {
                print("Torch could not be used")
            }
        } else {
            print("Torch is not available")
        }
    }

    func failed() {
        let ac = UIAlertController(title: "Scanning not supported", message: "Your device does not support scanning a code from an item. Please use a device with a camera.", preferredStyle: .alert)
        ac.addAction(UIAlertAction(title: "OK", style: .default))
        present(ac, animated: true)
        captureSession = nil
        toggleTorch(on: false)
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        if (captureSession?.isRunning == false) {
            captureSession.startRunning()
            toggleTorch(on: true)
        }
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

        if (captureSession?.isRunning == true) {
            captureSession.stopRunning()
            toggleTorch(on: false)
        }
    
        cameraScanDismissedCallback(scanned)
    }

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

        if let metadataObject = metadataObjects.first {
            guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else { return }
            guard let stringValue = readableObject.stringValue else { return }
            AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
            found(code: stringValue)
            scanned = true
        }

        dismiss(animated: true)
    }

    func found(code: String) {
        print(code)
        barcodeCallback(code)
    }

    override var prefersStatusBarHidden: Bool {
        return true
    }

    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        if(UIDevice.current.userInterfaceIdiom == .pad){
            return .landscape
        }else{
            return .portrait
        }
    }
}

【问题讨论】:

  • “不起作用”是什么意思?它怎么不工作?你的 iPad 有摄像头屏幕吗? AVCaptureSession 识别条形码但不返回东西时是否停止?
  • 是的,它根本没有检测到条形码。否则,视图会显示,扫描区域(黄色框)也会按预期显示。会话只是继续运行,因为它无法检测到。我试过把条码倒过来,扫描到扫描区域外等等,还是检测不到条码。
  • 你有几行 'if(UIDevice.current.userInterfaceIdiom == .pad){.'你为什么不看看没有他们会发生什么?有人用AVCaptureSession用iPad扫描条码。所以这不是确定使用 iPhone 或 iPad 的问题。
  • @LeoDabus 绝对正确。文档说“即使设备有手电筒,该手电筒也可能无法使用。因此,您还应该在使用前检查 isTorchAvailable 属性的值。”
  • iPad 肯定有一个手电筒,它按预期工作,但是是的,我可能无论如何都应该检查一下。 @ElTomato 至于 iPad 检查,我一开始就已经尝试过了,我一直在玩,因为定位与在 iPhone 上执行时略有不同。

标签: ios swift ipad barcode avcapturesession


【解决方案1】:

以防万一有人遇到类似问题,请检查以下内容:

  1. 视频的方向
  2. AVCaptureMetadataOutput.rectOfInterest的位置

我仍然遇到 rectOfInterest 未位于中心的问题,但它可以工作。一旦我弄清楚如何将其居中,我将在此处发布解决方案。

【讨论】:

    【解决方案2】:

    它不是中心可能是因为您的导航栏。 尝试如下设置rectOfInterest,记住camer使用的坐标与UIView不同,posX和posY在UIView坐标中

    let aimRect = CGRect(x: (posY - navBar.height) / UIScreen.main.bounds.height,
                         y: posX / UIScreen.main.bounds.width,
                         width: rectHeight / UIScreen.main.bounds.height,
                         height: rectWidth / UIScreen.main.bounds.width)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-05-18
      • 2011-03-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-08
      相关资源
      最近更新 更多