Сканирование штрих-кода или QR-кода в Swift 3.0 с помощью AVFoundation
Я следую этому уроку и попытался преобразовать коды из Swift 2.0 в 3.0. Но когда я запустил приложение, оно не работает! Я имею в виду, ничего не происходит! Вот мой код:
ViewController:
class ViewController: UIViewController ,BarcodeDelegate {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let barcodeViewController: BarcodeViewController = segue.destination as! BarcodeViewController
barcodeViewController.delegate = self
}
func barcodeReaded(barcode: String) {
codeTextView.text = barcode
print(barcode)
}
}
BarcodeVC:
import AVFoundation
protocol BarcodeDelegate {
func barcodeReaded(barcode: String)
}
class BarcodeViewController: UIViewController,AVCaptureMetadataOutputObjectsDelegate {
var delegate: BarcodeDelegate?
var captureSession: AVCaptureSession!
var code: String?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
print("works")
self.captureSession = AVCaptureSession();
let videoCaptureDevice: AVCaptureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
do {
let videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
if self.captureSession.canAddInput(videoInput) {
self.captureSession.addInput(videoInput)
} else {
print("Could not add video input")
}
let metadataOutput = AVCaptureMetadataOutput()
if self.captureSession.canAddOutput(metadataOutput) {
self.captureSession.addOutput(metadataOutput)
metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
metadataOutput.metadataObjectTypes = [AVMetadataObjectTypeQRCode,AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypePDF417Code]
} else {
print("Could not add metadata output")
}
let previewLayer = AVCaptureVideoPreviewLayer(session: self.captureSession)
previewLayer?.frame = self.view.layer.bounds
self.view.layer .addSublayer(previewLayer!)
self.captureSession.startRunning()
} catch let error as NSError {
print("Error while creating vide input device: \(error.localizedDescription)")
}
}
//I THINK THIS METHOD NOT CALL !
private func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!) {
// This is the delegate'smethod that is called when a code is readed
for metadata in metadataObjects {
let readableObject = metadata as! AVMetadataMachineReadableCodeObject
let code = readableObject.stringValue
// If the code is not empty the code is ready and we call out delegate to pass the code.
if code!.isEmpty {
print("is empty")
}else {
self.captureSession.stopRunning()
self.dismiss(animated: true, completion: nil)
self.delegate?.barcodeReaded(barcode: code!)
}
}
}
Вот вывод:
2016-09-17 18: 10: 26.000919 BarcodeScaning [2610: 674253] [MC] Контейнер системной группы для system group.com.apple.configurationprofiles путь является /private/var/containers/Shared/SystemGroup/system group.com.apple.configurationprofiles 2016-09-17 18: 10: 26.007782 BarcodeScaning [2610: 674253] [MC] Чтение из общедоступных эффективных пользовательских настроек.
5 ответов
Первым шагом должно быть объявление доступа к любым пользовательским частным типам данных, что является новым требованием в iOS 10. Это можно сделать, добавив ключ использования в приложение. Info.plist
вместе со строкой назначения.
Потому что, если вы используете одну из следующих платформ и не можете объявить об использовании, ваше приложение будет аварийно завершать работу при первом обращении:
Контакты, календарь, напоминания, фотографии, обмен Bluetooth, микрофон, камера, местоположение, здоровье, HomeKit, медиа-библиотека, Motion, CallKit, распознавание речи, SiriKit, TV Provider.
Чтобы избежать сбоя, необходимо добавить предложенный ключ к Info.plist
:
И тогда система показывает строку назначения, когда просит пользователя разрешить доступ:
Для получения дополнительной информации об этом вы можете использовать эту статью:
Я сделал несколько модификаций для вашего BarcodeViewController
чтобы он работал правильно, как вы можете видеть ниже:
BarcodeViewController
import UIKit
import AVFoundation
protocol BarcodeDelegate {
func barcodeReaded(barcode: String)
}
class BarcodeViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
var delegate: BarcodeDelegate?
var videoCaptureDevice: AVCaptureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
var device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
var output = AVCaptureMetadataOutput()
var previewLayer: AVCaptureVideoPreviewLayer?
var captureSession = AVCaptureSession()
var code: String?
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.clear
self.setupCamera()
}
private func setupCamera() {
let input = try? AVCaptureDeviceInput(device: videoCaptureDevice)
if self.captureSession.canAddInput(input) {
self.captureSession.addInput(input)
}
self.previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
if let videoPreviewLayer = self.previewLayer {
videoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
videoPreviewLayer.frame = self.view.bounds
view.layer.addSublayer(videoPreviewLayer)
}
let metadataOutput = AVCaptureMetadataOutput()
if self.captureSession.canAddOutput(metadataOutput) {
self.captureSession.addOutput(metadataOutput)
metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
metadataOutput.metadataObjectTypes = [AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN13Code]
} else {
print("Could not add metadata output")
}
}
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!) {
// This is the delegate'smethod that is called when a code is readed
for metadata in metadataObjects {
let readableObject = metadata as! AVMetadataMachineReadableCodeObject
let code = readableObject.stringValue
self.dismiss(animated: true, completion: nil)
self.delegate?.barcodeReaded(barcode: code!)
print(code!)
}
}
}
Одним из важных моментов было объявить глобальные переменные и запустить и остановить captureSession
внутри viewWillAppear(:)
а также viewWillDisappear(:)
методы. В вашем предыдущем коде я думаю, что он вообще не вызывался, так как он никогда не вводится внутри метода для обработки штрих-кода.
Я надеюсь, что это поможет вам.
Вот ответ Виктора Сиглера, обновленный до Swift 4 без принудительного развертывания, слабый протокол, выполнение дорогостоящего кода в фоновом потоке и другие уточнения.
Заметить, что AVCaptureMetadataOutputObjectsDelegate
метод изменился с
captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!)
в
metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection)
import UIKit
import AVFoundation
protocol BarcodeDelegate: class {
func barcodeRead(barcode: String)
}
class ScannerViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
weak var delegate: BarcodeDelegate?
var output = AVCaptureMetadataOutput()
var previewLayer: AVCaptureVideoPreviewLayer!
var captureSession = AVCaptureSession()
override func viewDidLoad() {
super.viewDidLoad()
setupCamera()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
DispatchQueue.global(qos: .background).async {
if !self.captureSession.isRunning {
self.captureSession.startRunning()
}
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
DispatchQueue.global(qos: .background).async {
if self.captureSession.isRunning {
self.captureSession.stopRunning()
}
}
}
fileprivate func setupCamera() {
guard let device = AVCaptureDevice.default(for: .video),
let input = try? AVCaptureDeviceInput(device: device) else {
return
}
DispatchQueue.global(qos: .background).async {
if self.captureSession.canAddInput(input) {
self.captureSession.addInput(input)
}
let metadataOutput = AVCaptureMetadataOutput()
if self.captureSession.canAddOutput(metadataOutput) {
self.captureSession.addOutput(metadataOutput)
metadataOutput.setMetadataObjectsDelegate(self, queue: .global(qos: .background))
if Set([.qr, .ean13]).isSubset(of: metadataOutput.availableMetadataObjectTypes) {
metadataOutput.metadataObjectTypes = [.qr, .ean13]
}
} else {
print("Could not add metadata output")
}
self.previewLayer = AVCaptureVideoPreviewLayer(session: self.captureSession)
self.previewLayer.videoGravity = .resizeAspectFill
DispatchQueue.main.async {
self.previewLayer.frame = self.view.bounds
self.view.layer.addSublayer(self.previewLayer)
}
}
}
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
// This is the delegate's method that is called when a code is read
for metadata in metadataObjects {
if let readableObject = metadata as? AVMetadataMachineReadableCodeObject,
let code = readableObject.stringValue {
dismiss(animated: true)
delegate?.barcodeRead(barcode: code)
print(code)
}
}
}
}
Сканер штрих-кода в Swift 4 для всех типов кодов
Ниже я хотел бы поделиться с несколькими идеями относительно сканирования штрих-кода в iOS.
- отделить логику сканера штрих-кода от логики просмотра,
- добавить запись в файл.plist
- задавать
exposurePointOfInterest
а такжеfocusPointOfInterest
- задавать
rectOfInterests
с правильно преобразованным CGRect - задавать
focusMode
а такжеexposureMode
- заблокировать захват устройства с
lockForConfiguration
правильно при изменении настроек захвата камеры
Добавить запись в файл.plist
В файле Info.plist добавьте следующий код, чтобы приложение могло получить доступ к камере iPhone:
<key>NSCameraUsageDescription</key>
<string>Allow access to camera</string>
Установите выставление PointOfInterest и focusPointOfInterestexposurePointOfInterest
а также focusPointOfInterest
Позволяет улучшить качество сканирования, быстрее фокусировать камеру на центральной точке экрана.
Установить rectOfInterests
Это свойство позволяет камере фокусироваться только на части экрана. Таким образом, код можно сканировать быстрее, ориентируясь только на коды, представленные в центре экрана - что полезно, хотя немногие другие коды доступны в фоновом режиме.
Установите focusMode и ject ionMode Properties должны быть установлены следующим образом:
device.focusMode = .continuousAutoFocus
device.exposureMode = .continuousAutoExposure
Это позволяет непрерывно фокусировать и устанавливать экспозицию, хорошо приспособленную к сканирующему коду.
демонстрация
Здесь вы можете найти готовый проект, реализующий эту идею: https://github.com/lukszar/QuickScanner
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
print("caught QR code")
for metadata in metadataObjects {
let readableObject = metadata as! AVMetadataMachineReadableCodeObject
let code = readableObject.stringValue
if code!.isEmpty {
print("is empty")
} else {
self.captureSession.stopRunning()
self.dismiss(animated: true, completion: nil)
self.delegate?.gotQRCode(code: code!)
}
}
}
Похоже, подпись метода немного изменилась в Swift 3. Вот правильная версия
Вам нужно добавить NSCameraUsageDescription в ваш файл Info.plist, чтобы он заработал!
Просто добавьте строку в info.plist и введите NSCameraUsageDescription во вновь созданную строку и добавьте строку, предназначенную для информирования пользователя о том, почему в вашем приложении необходим доступ к камере.
Это должно сработать!