Barcode on swift 4
Solution 1
I figured it out but Apple didn't make it so obvious. The callback function from the delegate AVCaptureMetadataOutputObjectsDelegate has been renamed and the parameter names are different!
So, replace
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!)
to
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection)
My view controller is now scanning QR Codes as before after this. It has the same parameters but the first parameter name is different. Change the function and parameter names and build/run.
Hope this helps!
Solution 2
After changing the delegate call back :
From
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!)
To
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection)
I need to set all available types for metadataObjectTypes too as below-
output.metadataObjectTypes=output.availableMetadataObjectTypes
Solution 3
After changing your code from:
func metadataOutput(captureOutput: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {}
to:
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {}
everything works again.
Related videos on Youtube
Dx_
Updated on December 04, 2020Comments
-
Dx_ over 3 years
I'm trying to upgrade mi app to swift 4, but the barcode reader is not working.
I have isolated the barcode reader code, and still not working. The camera works but it does not detect the barcode.
The code worked just fine on swift 3 iOS 10.
This is the complete code
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 } }
I am using iOS 11 on my iPhone, upgraded to beta 9.
Any idea? Thank you.
-
Mario A Guzman almost 7 yearsSo it's nice to know that this problem isn't just happening to me after updating to iOS 11 and Swift 4 for my project. I have a very basic QR code reader as well in my app using an AVCaptureMetadataOutput object and the AVCaptureMetadataOutputObjectsDelegate delegate. I have verified that everything is constantly and consistently running and not interrupted. I think at this point its time to submit a bug to Apple (both of use should). Only thing that changed were the names of properties/functions in Swift 4 but nothing else. Weird that we're not getting any delegate callbacks.
-
Mario A Guzman almost 7 yearsAlso, looking at your code, you need to create a Serial Queue for your AVCaptureMetadataOutputObjectsDelegate callback. metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main). Instead of using the main queue, create a serial queue as a property in your view controller and use it here rather than the main queue.
-
Mahendra about 6 yearsJust for reference, can be used third party github.com/mahendragp/MGPBarcodeScanner
-
-
Dx_ almost 7 yearsThat was the solution! The function is different, thank you!
-
Mario A Guzman almost 7 yearsI submitted an enhancement request to Apple so that Xcode will give developers a heads up if a function changes like that. I spent so long not finding the solution until I got to the API and read it line by line because no way was my code wrong. And It wasn't! That's when I realized the function name was different.
-
Andrew Bennet almost 7 yearsI additionally had to use a non-Main DispatchQueue when setting the metadataObjectsDelegate, in order to get the callback to occur:
output.setMetadataObjectsDelegate(self, queue: DispatchQueue.global(qos: .userInteractive))
-
Mario A Guzman almost 7 years@AndrewBennet Right, I mentioned this to him also in comment attached to his question above. You have to create and pass in a Serial Queue as a param. Define one globally and pass it in.
-
Leon over 6 yearsWhy did you answer with the same info as Mario?
-
Emil Korngold over 6 yearsAffirmation of correct answer by Mario, please next time simply up-vote the correct answer. Thanks!
-
Jason Moore over 6 yearsI can confirm that the updated delegate works on iOS9 and iOS10.
-
mbonness over 6 yearsThis answer worked better for me since it is more obvious that the function signature was changed, not just the function name.
-
mbonness over 6 yearsJust renaming the function is insufficient, you also need to change the first parameter name from captureOutput to output for it to be called.
-
Mario A Guzman over 6 years@mbonness - yes, that is shown in my sample code above.
-
mbonness over 6 years@MarioAGuzman Got it thanks. It looks like actually the method signature is changed, the first parameter type is different, in case you want to call that out in your answer.
-
btrballin about 5 yearsThis answer worked for me over the accepted answer. In addition to changing the function signature, updating the line to
output.metadataObjectTypes=output.availableMetadataObjectTypes
was ultimately what allowed my scanner to return a value -
Ben Butzer over 4 yearsIn iOS 13.0 and 13.1 with Xamarin I ran into an issue where I had to explicitly set the bar code types again, as the line of code would crash: output.metadataObjectTypes=output.availableMetadataObjectTypes
-
Ben Butzer over 4 yearsAdd to my comment above, it was only on the iPhone 11 pro, where this happened.