Штрих-код на Swift 4

Я пытаюсь обновить приложение mi до Swift 4, но сканер штрих-кода не работает.

Я изолировал код считывателя штрих-кода, и он все еще не работает. Камера работает, но штрих-код не распознает.

Код отлично работал на Swift 3 iOS 10.

Это полный код

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
}
}

Я использую iOS 11 на своем iPhone, обновленную до бета-версии 9.

Любая идея? Спасибо.


person Dx_    schedule 02.09.2017    source источник
comment
Поэтому приятно знать, что эта проблема возникает не только у меня после обновления до iOS 11 и Swift 4 для моего проекта. У меня также есть очень простой считыватель QR-кода в моем приложении, использующий объект AVCaptureMetadataOutput и делегат AVCaptureMetadataOutputObjectsDelegate. Я проверил, что все постоянно и стабильно работает и не прерывается. Я думаю, что на данный момент пришло время сообщить об ошибке в Apple (оба должны использовать). Единственное, что изменилось, это названия свойств/функций в Swift 4, но больше ничего. Странно, что мы не получаем обратных вызовов делегатов.   -  person Mario A Guzman    schedule 03.09.2017
comment
Кроме того, глядя на ваш код, вам нужно создать последовательную очередь для обратного вызова AVCaptureMetadataOutputObjectsDelegate. metadataOutput.setMetadataObjectsDelegate(я, очередь: DispatchQueue.main). Вместо использования основной очереди создайте последовательную очередь как свойство в вашем контроллере представления и используйте ее здесь, а не основную очередь.   -  person Mario A Guzman    schedule 03.09.2017
comment
Просто для справки, можно использовать сторонний github.com/mahendragp/MGPBarcodeScanner.   -  person Mahendra    schedule 31.05.2018


Ответы (4)


Я понял это, но Apple не сделала это настолько очевидным. Функция обратного вызова из делегата AVCaptureMetadataOutputObjectsDelegate была переименована, а имена параметров другие!

Итак, замените

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

to

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

После этого мой контроллер просмотра теперь сканирует QR-коды, как и раньше. У него те же параметры, но имя первого параметра другое. Измените имена функций и параметров и выполните сборку/запуск.

Надеюсь это поможет!

person Mario A Guzman    schedule 03.09.2017
comment
Это было решением! Функция другая, спасибо! - person Dx_; 06.09.2017
comment
Я отправил запрос на улучшение в Apple, чтобы Xcode сообщал разработчикам, если функция изменится таким образом. Я так долго не мог найти решение, пока не добрался до API и не прочитал его построчно, потому что мой код никоим образом не был неправильным. И это было не так! Именно тогда я понял, что имя функции было другим. - person Mario A Guzman; 06.09.2017
comment
Кроме того, мне пришлось использовать неосновную DispatchQueue при установке metadataObjectsDelegate, чтобы вызвать обратный вызов: output.setMetadataObjectsDelegate(self, queue: DispatchQueue.global(qos: .userInteractive)) - person Andrew Bennet; 08.09.2017
comment
@AndrewBennet Верно, я упомянул об этом также в комментарии к его вопросу выше. Вы должны создать и передать последовательную очередь в качестве параметра. Определите один глобально и передайте его. - person Mario A Guzman; 08.09.2017
comment
Я могу подтвердить, что обновленный делегат работает на iOS9 и iOS10. - person Jason Moore; 12.10.2017
comment
Просто переименовать функцию недостаточно, вам также нужно изменить имя первого параметра с captureOutput на output, чтобы она вызывалась. - person mbonness; 20.10.2017
comment
@mbonness - да, это показано в моем примере кода выше. - person Mario A Guzman; 24.10.2017
comment
@MarioAGuzman Понял, спасибо. Похоже, что на самом деле сигнатура метода изменена, первый тип параметра отличается, если вы хотите вызвать это в своем ответе. - person mbonness; 24.10.2017

После изменения обратного вызова делегата:

От

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

To

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

Мне также нужно установить все доступные типы для metadataObjectTypes, как показано ниже:

output.metadataObjectTypes=output.availableMetadataObjectTypes
person Anindya    schedule 21.03.2018
comment
Этот ответ сработал для меня над принятым ответом. В дополнение к изменению сигнатуры функции обновление строки до output.metadataObjectTypes=output.availableMetadataObjectTypes в конечном итоге позволило моему сканеру вернуть значение - person btrballin; 28.05.2019
comment
В iOS 13.0 и 13.1 с Xamarin я столкнулся с проблемой, когда мне пришлось снова явно задавать типы штрих-кода, так как строка кода вылетала: output.metadataObjectTypes=output.availableMetadataObjectTypes - person Ben Butzer; 26.09.2019
comment
Добавьте к моему комментарию выше, это было только на iPhone 11 pro, где это произошло. - person Ben Butzer; 26.09.2019

После изменения кода с:

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

to:

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

все снова работает.

person jl.medel    schedule 24.09.2017
comment
Почему вы ответили той же информацией, что и Марио? - person Leon; 01.10.2017
comment
Подтверждение правильного ответа Марио, пожалуйста, в следующий раз просто проголосуйте за правильный ответ. Спасибо! - person Emil Korngold; 04.10.2017
comment
Этот ответ сработал для меня лучше, так как более очевидно, что была изменена сигнатура функции, а не только имя функции. - person mbonness; 20.10.2017

Вы можете использовать QRCodeScanner83 для сканирования штрих-кодов:

import QRCodeScanner83
import AVFoundation

...

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

Если вам нужен пользовательский интерфейс, вы можете вложить его из CodeScannerViewController и установить CodeScannerViewController.delegate для получения обновлений состояния сканера.

person Alexander Volkov    schedule 03.12.2020