44 votes

Code-barres sur swift 4

Je suis en train de mettre à jour mon application en swift 4, mais le lecteur de code-barres ne fonctionne pas.

J'ai isolé le code du lecteur de code-barres, mais il ne fonctionne toujours pas. La caméra fonctionne mais ne détecte pas le code-barres.

Le code fonctionnait très bien sur swift 3 iOS 10.

Voici le code complet

import AVFoundation
import UIKit

class ViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
var captureSession: AVCaptureSession!
var previewLayer: AVCaptureVideoPreviewLayer!

override func viewDidLoad() {
    super.viewDidLoad()

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

    let videoCaptureDevice = AVCaptureDevice.default(for: AVMediaType.video)
    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 = [AVMetadataObject.ObjectType.ean8, AVMetadataObject.ObjectType.ean13, AVMetadataObject.ObjectType.pdf417]
    } else {
        failed()
        return
    }

    previewLayer = AVCaptureVideoPreviewLayer(session: captureSession);
    previewLayer.frame = view.layer.bounds;
    previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill;
    view.layer.addSublayer(previewLayer);

    captureSession.startRunning();
}

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
}

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

    if (captureSession?.isRunning == false) {
        captureSession.startRunning();
    }
}

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

    if (captureSession?.isRunning == true) {
        captureSession.stopRunning();
    }
}

func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
    captureSession.stopRunning()

    if let metadataObject = metadataObjects.first {
        let readableObject = metadataObject as! AVMetadataMachineReadableCodeObject;

        AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
        found(code: readableObject.stringValue!);
    }

    dismiss(animated: true)
}

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

override var prefersStatusBarHidden: Bool {
    return true
}

override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    return .portrait
}
}

Je suis sur iOS 11 sur mon iPhone, mis à jour en beta 9.

Une idée? Merci.

2 votes

Il est donc agréable de savoir que ce problème ne m'arrive pas seulement après la mise à jour vers iOS 11 et Swift 4 pour mon projet. J'ai aussi un lecteur de code QR très basique dans mon application utilisant un objet AVCaptureMetadataOutput et le délégué AVCaptureMetadataOutputObjectsDelegate. J'ai vérifié que tout fonctionne constamment et sans interruption. Je pense qu'à ce stade, il est temps de soumettre un bug à Apple (nous devrions le faire tous les deux). La seule chose qui a changé sont les noms des propriétés/fonctions en Swift 4 mais rien d'autre. Étrange que nous ne recevions aucun rappel du délégué.

1 votes

De plus, en examinant votre code, vous devez créer une file d'attente série pour votre rappel AVCaptureMetadataOutputObjectsDelegate. metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main). Au lieu d'utiliser la file d'attente principale, créez une file d'attente sérielle en tant que propriété dans votre contrôleur de vue et utilisez-la ici plutôt que la file d'attente principale.

0 votes

Juste pour référence, vous pouvez utiliser un code à barres tiers github.com/mahendragp/MGPBarcodeScanner

93voto

Mario A Guzman Points 1692

J'ai compris mais Apple ne l'a pas rendu si évident. La fonction de rappel du délégué AVCaptureMetadataOutputObjectsDelegate a été renommée et les noms des paramètres sont différents !

Donc, remplacez

func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!)

par

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

Mon contrôleur de vue scanne maintenant les codes QR comme avant. Il a les mêmes paramètres mais le nom du premier paramètre est différent. Modifiez les noms de fonction et de paramètre et compilez/exécutez.

0 votes

C'était la solution! La fonction est différente, merci!

3 votes

J'ai soumis une demande d'amélioration à Apple pour que Xcode avertisse les développeurs si une fonction change de cette manière. J'ai passé tellement de temps à ne pas trouver de solution jusqu'à ce que j'arrive à l'API et que je la lise ligne par ligne car il n'y avait pas moyen que mon code soit faux. Et ce n'était pas le cas ! C'est à ce moment-là que j'ai réalisé que le nom de la fonction était différent.

1 votes

J'ai également dû utiliser une file d'attente non Main DispatchQueue lors de la définition du metadataObjectsDelegate, afin que le rappel se produise : output.setMetadataObjectsDelegate(self, queue: DispatchQueue.global(qos: .userInteractive))

6voto

Anindya Points 256

Après avoir modifié le rappel de délégué :

De

func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!)

À

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

Je dois également définir tous les types disponibles pour metadataObjectTypes comme ci-dessous-

output.metadataObjectTypes=output.availableMetadataObjectTypes

0 votes

Cette réponse a fonctionné pour moi plutôt que la réponse acceptée. En plus de changer la signature de la fonction, mettre à jour la ligne en output.metadataObjectTypes=output.availableMetadataObjectTyp‌​es a finalement permis à mon scanner de renvoyer une valeur

0 votes

Dans iOS 13.0 et 13.1 avec Xamarin, j'ai rencontré un problème où j'ai dû définir explicitement à nouveau les types de codes barres, car la ligne de code plantait : output.metadataObjectTypes=output.availableMetadataObjectTyp‌​es

1 votes

Ajoutez à mon commentaire ci-dessus, c'était uniquement sur l'iPhone 11 Pro que cela s'est produit.

5voto

jl.medel Points 61

Après avoir changé votre code de:

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

à:

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

tout fonctionne à nouveau.

1 votes

Pourquoi avez-vous répondu avec les mêmes informations que Mario ?

0 votes

Affirmation de la réponse correcte de Mario, merci de simplement voter pour la réponse correcte la prochaine fois. Merci!

1 votes

Cette réponse a mieux fonctionné pour moi car il est plus évident que la signature de la fonction a été modifiée, pas seulement le nom de la fonction.

0voto

Alexander Volkov Points 241

Vous pouvez utiliser QRCodeScanner83 pour scanner des codes-barres :

import QRCodeScanner83
import AVFoundation

...

guard let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(identifier: "CodeScannerViewController") as? CodeScannerViewController else {
    return
}
vc.callbackCodeScanned = { code in
    print("CODE BALAYÉ : \(code)")
    vc.dismiss(animated: true, completion: nil)
}
self.present(vc, animated: true, completion: nil)

Si vous avez besoin d'une UI personnalisée, alors vous pouvez imbriquer depuis CodeScannerViewController et définir CodeScannerViewController.delegate pour recevoir les mises à jour de l'état du scanner.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X