Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scanning Barcode or QR code in Swift 3.0 using AVFoundation

I am following this tutorial and tried to convert codes form Swift 2.0 to 3.0. But when I launched the application, the app doesn't work! I mean, nothing happens! Here is my code:

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!)


            }
        }

    }

Here is the output:

2016-09-17 18:10:26.000919 BarcodeScaning[2610:674253] [MC] System group container for systemgroup.com.apple.configurationprofiles path is /private/var/containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles 2016-09-17 18:10:26.007782 BarcodeScaning[2610:674253] [MC] Reading from public effective user settings.

like image 946
iOS.Lover Avatar asked Sep 17 '16 13:09

iOS.Lover


People also ask

How do I scan a QR code with Swift?

Set the key to Privacy - Camera Usage Description, and value to We need to access your camera for scanning QR code. Once you finish the editing, deploy the app and run it on a real device again. Tapping the scan button should bring up the built-in camera and start capturing video.

How do I set up QR code scanning?

On your compatible Android phone or tablet, open the built-in camera app. Point the camera at the QR code. Tap the banner that appears on your Android phone or tablet. Follow the instructions on the screen to finish signing in.

Is 3D barcode same as QR code?

A: The 3D barcode is a common misconception. Many people think QR Codes and DataMatrix Codes are 3D when they are not. Neither are Tier 2 barcodes, which are multi-colored squares arranged in a rectangular pattern for added security. Those are still 2D barcodes.


2 Answers

Barcode Scanner in Swift 4 for all code types

Below I would like to share with few ideas according to barcode scanning in iOS.

  • separate barcode scanner logic from View logic,
  • add entry in .plist file
  • set exposurePointOfInterest and focusPointOfInterest
  • set rectOfInterests with proper converted CGRect
  • set focusMode and exposureMode
  • lock captureDevice with lockForConfiguration properly while change camera capture settings

Add entry in .plist file
In Info.plist file add following code to allow your application to access iPhone's camera:

<key>NSCameraUsageDescription</key>
<string>Allow access to camera</string>

Set exposurePointOfInterest and focusPointOfInterest
exposurePointOfInterest and focusPointOfInterest allow to better quality of scanning, faster focusing camera on central point of screen.

Set rectOfInterests
This property let camera to focus just on a part of the screen. This way code can be scanned faster, focused just on codes presented in a center of the screen - what is useful while few other codes are available in background.

Set focusMode and exposureMode Properties should be set like following:

device.focusMode = .continuousAutoFocus
device.exposureMode = .continuousAutoExposure

This allow to continuously focus and set exposure well adjusted to scanning code.

Demo

Here you can find ready project implementing this idea: https://github.com/lukszar/QuickScanner

like image 102
lukszar Avatar answered Oct 17 '22 03:10

lukszar


The first step needs to be declare access to any user private data types that is a new requirement in iOS 10. You can do it by adding a usage key to your app’s Info.plist together with a purpose string.

Because if you are using one of the following frameworks and fail to declare the usage your app will crash when it first makes the access:

Contacts, Calendar, Reminders, Photos, Bluetooth Sharing, Microphone, Camera, Location, Health, HomeKit, Media Library, Motion, CallKit, Speech Recognition, SiriKit, TV Provider.

To avoid the crash you need to add the suggested key to Info.plist:

enter image description here

And then the system shows the purpose string when asking the user to allow access:

enter image description here

For more information about it you can use this article:

  • Privacy Settings in iOS 10

I have done a little modifications to your BarcodeViewController to make it work properly as you can see below:

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's method that is called when a code is read
       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!)
       }
   }
}

One of the important points was to declare the global variables and start and stop the captureSession inside the viewWillAppear(:) and viewWillDisappear(:) methods. In your previous code I think it was not called at all as it never enter inside the method to process the barcode.

I hope this help you.

like image 35
Victor Sigler Avatar answered Oct 17 '22 02:10

Victor Sigler