My View disappear when i scan a QR code - ios

i am using the AVFoundation for the first time, and i have gotten my script working kinda...
I am making a ticket app, witch are scanning a QR code on the tickets and checks the database if its used. (I have hidden my API logins). But as soon as i scans the qr code, my code runs, i get a ticket Valid plottet in the console, but the the viewcontroller dissmisses, and goes back to my login page.
I am using this code, for my login page to show the viewcontroller with the QR code.
let viewController:UIViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "scanner") as UIViewController
self.present(viewController, animated: false, completion: nil)
And the the viewcontroller with the QR Scanner.
//
// scanner.swift
// GB Billet Scanner
//
// Created by Benjamin Eibye on 26/12/2017.
// Copyright © 2017 GymBilletter.dk. All rights reserved.
//
import UIKit
import AVFoundation
import Alamofire
class scanner: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
#IBOutlet weak var videoPreview: UIView!
#IBOutlet weak var navnLabel: UILabel!
let avCaptureSession = AVCaptureSession()
var returnValue: String = String()
enum error: Error {
case noCameraAvailable
case videoInputInitFail
}
override func viewDidLoad() {
super.viewDidLoad()
// Creating session
do {
try scanQRCode()
} catch {
print("Failed to scan the QR/BarCode")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
avCaptureSession.stopRunning()
if let metadataObject = metadataObjects.first {
let readableObject = metadataObject as! AVMetadataMachineReadableCodeObject;
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
//print(readableObject.stringValue!);
navnLabel.text = "Indlæser billet #\(readableObject.stringValue!)"
let urlEnc = "https://domain?token=\(String(describing: UserDefaults.standard.object(forKey: "token")!))&ticket=\(readableObject.stringValue!)"
Alamofire.request(urlEnc).responseJSON { response in
//print(urlEnc)
//debugPrint(response)
//print(response.result)
if let result = response.result.value {
let JSON = result as! NSDictionary
if JSON["error"] as? Int == 5002 {
print("User not Authendicated")
print(urlEnc)
self.navnLabel.text = "Telefonen er ikke logget ind"
self.avCaptureSession.startRunning()
} else if JSON["error"] as? Int == 5003 {
print("Ticket not Found")
self.navnLabel.text = "Billetten blev ikke fundet"
self.avCaptureSession.startRunning()
} else {
// Billetten eksistere
print("Billet fundet")
if JSON["active"] as! String == "1" {
print("ticket Valid")
self.avCaptureSession.startRunning()
} else {
print("Billetten er blevet brugt")
self.navnLabel.text = "Billetten er blevet brugt \(String(describing: JSON["used_date"]))"
}
}
}
}
}
dismiss(animated: true)
}
func scanQRCode() throws {
guard let avCaptureDevice = AVCaptureDevice.default(for: AVMediaType.video) else {
print("no camera.")
throw error.noCameraAvailable
}
guard let avCaptureInput = try? AVCaptureDeviceInput(device: avCaptureDevice) else {
print("Faild to init camera.")
throw error.videoInputInitFail
}
let avCaptureMetadataOutput = AVCaptureMetadataOutput()
avCaptureMetadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
avCaptureSession.addInput(avCaptureInput)
avCaptureSession.addOutput(avCaptureMetadataOutput)
avCaptureMetadataOutput.metadataObjectTypes = [AVMetadataObject.ObjectType.qr]
let avCaptureVideoPreviewLayer = AVCaptureVideoPreviewLayer(session: avCaptureSession)
avCaptureVideoPreviewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
avCaptureVideoPreviewLayer.frame = videoPreview.bounds
self.videoPreview.layer.addSublayer(avCaptureVideoPreviewLayer)
avCaptureSession.startRunning()
}
#IBAction func anuller(_ sender: Any) {
navnLabel.text = "Scan billet."
avCaptureSession.startRunning()
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}

The dismiss(animated: true) at the end of your metadataOutput(...) function is being called regardless of what returned.

Related

Presenting UIViewController in UIView

I have recently started with ios development. So currently I have a requirement to present a QR scanner view when a view is clicked. I came through one library https://github.com/twostraws/CodeScanner. but it doesn't work.
I have also the QR code implementation
import AVFoundation
import UIKit
class QRCode: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
var captureSession: AVCaptureSession!
var previewLayer: AVCaptureVideoPreviewLayer!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
view.backgroundColor = UIColor.black
captureSession = AVCaptureSession()
guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else { return }
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 = [.ean8, .ean13, .pdf417]
} else {
failed()
return
}
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.frame = view.layer.bounds
previewLayer.videoGravity = .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()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//func captureOutput(captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, fromConnection connection: AVCaptureConnection!)
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
captureSession.stopRunning()
if let metadataObject = metadataObjects.first {
guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else { return }
guard let stringValue = readableObject.stringValue else { return }
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
found(code: stringValue)
}
dismiss(animated: true)
}
func found(code: String) {
print(code)
}
override var prefersStatusBarHidden: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .portrait
}
}
but I am not able to use this inside a class which inherits UIView
The class definition in which I am trying to use the QRCode class
class A: UIView
I have recently started with ios. Any help would be great
if you want to present a view controller from a presented view controller you can do the following
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func presentPressed(sender: Any) {
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let presentedVC = mainStoryboard.instantiateViewController(withIdentifier: "presentedViewController")
present(presentedVC, animated: true, completion: nil)
}
}
class presentedViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func presentQRVCPressed(sender: Any) {
let mainStoryboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let qrVC = mainStoryboard.instantiateViewController(withIdentifier: "QRViewController")
present(qrVC, animated: true, completion: nil)
}
}
class QRViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
otherwise if you want to present a view controller when clicking inside a view, do the following
protocol yourCustomViewDelegate: class {
func presentQRPressed()
}
class yourCustomView: UIView {
// MARK: - Properties
weak var delegate: yourCustomViewDelegate?
#IBAction func presentQRButton(sender: Any) {
if let delegate = delegate {
delegate.presentQRPressed()
}
}
}
class yourViewController: UIViewController, yourCustomViewDelegate {
func presentQRPressed() {
present(yourQRVC, animated: true, completion: nil)
}
}

iOS barcode scanner App crashes with no crash log

I am trying to make a barcode scanner app. As soon as the camera session begins, the app crashes within a few seconds.
I am unable to find the reason behind this. and, how to fix this one.
I have used https://www.appcoda.com/barcode-reader-swift/ to make the barcde scanner.
import Foundation
import UIKit
import AVFoundation
import CoreData
enum BarcodeScanError : String{
case cameraLoadFailed = "Camera Load Failed"
case NoValidBarcode = "No Valid Barcode"
}
class ScanBoardingPassViewController : UIViewController {
//MARK: - Properties
var viewModel : ScanBoardingPassViewModel? = nil
var captureSession : AVCaptureSession?
var videoPreviewLayer: AVCaptureVideoPreviewLayer?
var qrCodeFrameView: UIView?
private let supportedCodeTypes = [AVMetadataObject.ObjectType.aztec,
AVMetadataObject.ObjectType.pdf417]
//MARK: - Outlets
#IBOutlet weak var btnCancel: UIButton!
//MARK: - View Life Cycle
override func viewDidLoad() {
viewModel = ScanBoardingPassViewModel()
self.captureSession = AVCaptureSession()
self.setUpView()
super.viewDidLoad()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//MARK: - Set Up View
func setUpView() {
self.setUpBarCodeScanner()
self.view.bringSubviewToFront(self.btnCancel)
self.setUpBarcodeRecognizerFrame()
}
private func setUpBarCodeScanner() {
// Get the back-facing camera for capturing videos
guard let captureDevice = AVCaptureDevice.default(for: AVMediaType.video) else {
debugPrint(BarcodeScanError.cameraLoadFailed)
return
}
do {
// Get an instance of the AVCaptureDeviceInput class using the previous device object.
let input = try AVCaptureDeviceInput(device: captureDevice)
// Set the input device on the capture session.
captureSession?.addInput(input)
// Initialize a AVCaptureMetadataOutput object and set it as the output device to the capture session.
let captureMetadataOutput = AVCaptureMetadataOutput()
captureSession?.addOutput(captureMetadataOutput)
// Set delegate and use the default dispatch queue to execute the call back
captureMetadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
captureMetadataOutput.metadataObjectTypes = supportedCodeTypes
// captureMetadataOutput.metadataObjectTypes = [AVMetadataObject.ObjectType.qr]
} catch {
// If any error occurs, simply print it out and don't continue any more.
print(error)
return
}
// Initialize the video preview layer and add it as a sublayer to the viewPreview view's layer.
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession!)
videoPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
videoPreviewLayer?.frame = view.layer.bounds
view.layer.addSublayer(videoPreviewLayer!)
// Start video capture.
captureSession?.startRunning()
}
private func setUpBarcodeRecognizerFrame() {
// Initialize QR Code Frame to highlight the QR code
qrCodeFrameView = UIView()
if let qrCodeFrameView = qrCodeFrameView {
qrCodeFrameView.layer.borderColor = UIColor.green.cgColor
qrCodeFrameView.layer.borderWidth = 2
view.addSubview(qrCodeFrameView)
view.bringSubviewToFront(qrCodeFrameView)
}
}
//MARK: - Outlets
#IBAction func btnCancelPressed(_ sender: UIButton) {
self.dismissView()
}
func dismissView() {
self.dismiss(animated: true, completion: nil)
}
}
extension ScanBoardingPassViewController: AVCaptureMetadataOutputObjectsDelegate {
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
// Check if the metadataObjects array is not nil and it contains at least one object.
if metadataObjects.count == 0 {
qrCodeFrameView?.frame = CGRect.zero
debugPrint(BarcodeScanError.NoValidBarcode)
return
}
// Get the metadata object.
let metadataObj = metadataObjects[0] as! AVMetadataMachineReadableCodeObject
if supportedCodeTypes.contains(metadataObj.type) {
// If the found metadata is equal to the QR code metadata (or barcode) then update the status label's text and set the bounds
let barCodeObject = videoPreviewLayer?.transformedMetadataObject(for: metadataObj)
qrCodeFrameView?.frame = barCodeObject!.bounds
if metadataObj.stringValue != nil {
captureSession?.stopRunning()
debugPrint("Valid Barcode found \(metadataObj.stringValue!)")
if let boardingPass = viewModel?.parseBoardingPassString(boardingPassString : metadataObj.stringValue!) {
let unitOfWork = UnitOfWork(context:( UIApplication.shared.delegate as! AppDelegate).persistentContainer.newBackgroundContext() )
unitOfWork.boardingPassRepository.saveBoardingPasses(boardingPass: boardingPass)
unitOfWork.saveChanges()
print(unitOfWork.boardingPassRepository.getBoardingPasses(predicate: nil))
self.dismissView()
}
}
}
}
}
The camera doesnt get struck. But, the app gives an lldb everytime within a few seconds.
// Created by Satya Narayana on 17/11/20.
//
import UIKit
import AVFoundation
import UIKit
class QRViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
//MARK: Outlets
#IBOutlet weak var qrLbl: UILabel! // BarCode displaying Label
#IBOutlet weak var sView: UIView! // View
//MARK: Variables
var captureSession: AVCaptureSession!
var previewLayer: AVCaptureVideoPreviewLayer!
//MARK: View Methods
override func viewDidLoad() {
super.viewDidLoad()
captureSession = AVCaptureSession()
guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else { return }
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 = [.qr, .ean13, .code128]
} else {
failed()
return
}
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.frame = sView.layer.bounds
previewLayer.videoGravity = .resizeAspectFill
sView.layer.addSublayer(previewLayer)
captureSession.startRunning()
}
func failed() {
self.showToast(message: "Scanning not supported.Your device does not support scanning a code from an item. Please use a device with a camera.", seconds: 1.0)
captureSession = nil
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
qrLbl.isHidden = true
if (captureSession?.isRunning == false) {
captureSession.startRunning()
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if (captureSession?.isRunning == true) {
captureSession.stopRunning()
}
}
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
captureSession.stopRunning()
if let metadataObject = metadataObjects.first {
guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else { return }
guard let stringValue = readableObject.stringValue else { return }
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
found(code: stringValue)
}
dismiss(animated: true)
}
//MARK:- Found BARCODE
func found(code: String) {
print(code)
if code != ""{
print(code) // This is Barcode
qrLbl.text = code
}else{
// if you need run again uncomment below line
//self.captureSession.startRunning()
}
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .portrait
}
}

Bewildered: Non-nil value unwrapped nil, if-let clause executes block on nil value

I'm assigning a non-nil value of type Data to a non-optional property, which then assigns it to an optional property, which finally instantiates an image with said data. When the optional passes through an if-let clause, it's block executes, throwing an error:
Fatal error: Unexpectedly found nil while unwrapping an Optional value
By no means should stillImageData unwrap nil.
import UIKit
import AVFoundation
import Photos
class ViewController: UIViewController, AVCapturePhotoCaptureDelegate {
var photoOutput: AVCapturePhotoOutput!
var stillImageData: Data!
#IBAction func handleTouch(_ sender: Any) {
let photoSettings = AVCapturePhotoSettings(format: nil)
photoSettings.isAutoStillImageStabilizationEnabled = true
if photoOutput.supportedFlashModes.contains(AVCaptureDevice.FlashMode.auto) {
photoSettings.flashMode = .auto
}
photoOutput.capturePhoto(with: photoSettings, delegate: self)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
switch AVCaptureDevice.authorizationStatus(for: .video) {
case .authorized: // The user has previously granted access to the camera.
self.setupCaptureSession()
case .notDetermined: // The user has not yet been asked for camera access.
AVCaptureDevice.requestAccess(for: .video) { granted in
if granted {
self.setupCaptureSession()
}
}
case .denied: // The user has previously denied access.
return
case .restricted: // The user can't grant access due to restrictions.
return
}
PHPhotoLibrary.requestAuthorization { status in
guard status == .authorized else { return }
// Use PHPhotoLibrary.shared().performChanges(...) to add assets.
}
}
func setupCaptureSession() {
let captureSession = AVCaptureSession()
// Connect inputs and outputs to the session
captureSession.beginConfiguration()
let videoDevice = AVCaptureDevice.default(.builtInWideAngleCamera,
for: .video, position: .unspecified)
guard
let videoDeviceInput = try? AVCaptureDeviceInput(device: videoDevice!),
captureSession.canAddInput(videoDeviceInput)
else { return }
captureSession.addInput(videoDeviceInput)
photoOutput = AVCapturePhotoOutput()
guard captureSession.canAddOutput(photoOutput) else { return }
captureSession.sessionPreset = .photo
captureSession.addOutput(photoOutput)
captureSession.commitConfiguration()
// Display a camera preview
let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.frame = view.bounds
previewLayer.videoGravity = .resizeAspectFill
view.layer.addSublayer(previewLayer)
// Run the capture session
captureSession.startRunning()
}
// MARK: - Photo capture
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
// guard error != nil else { print("Error capturing photo: \(error!)"); return }
self.stillImageData = photo.fileDataRepresentation()
performSegue(withIdentifier: "showDetail", sender: self)
}
// MARK: - Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
let controller = (segue.destination as! UINavigationController).topViewController as! RootViewController
controller.detailItem = stillImageData
}
}
}
import UIKit
import Photos
class RootViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
func configureView() {
if let stillImageData = detailItem {
imageView.image = UIImage(data: stillImageData)
}
}
var detailItem: Data? {
didSet {
configureView()
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func dismissViewController(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
#IBAction func savePhotoToPhotosLibrary(_ sender: Any) {
PHPhotoLibrary.requestAuthorization { status in
guard status == .authorized else { return }
PHPhotoLibrary.shared().performChanges({
// Add the captured photo's file data as the main resource for the Photos asset.
let creationRequest = PHAssetCreationRequest.forAsset()
creationRequest.addResource(with: .photo, data: self.detailItem!, options: nil)
}, completionHandler: { success, error in
if !success { NSLog("error creating asset: \(error!)") }
})
}
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}
If anybody could lend a hand, it'd be greatly appreciated.
1) Why, if stillImageData and detailItem are assigned non-nil values, are they nil, and 2) why is the if-let clause executing its block?
stillImageData and detailItem are not nil, but imageView is nil.
In prepare(for at the moment you execute controller.detailItem = stillImageData the view of the destination controller is not loaded yet, therefore all outlets are not connected. When the outlet is accessed in configureView() the crash occurs.
In this case call configureView() in viewDidLoad
var detailItem: Data?
override func viewDidLoad() {
super.viewDidLoad()
configureView()
}
On the other hand if detailItem is going to be updated multiple times check the outlet
var detailItem: Data? {
didSet {
if imageView != nil { configureView() }
}
}
override func viewDidLoad() {
super.viewDidLoad()
configureView()
}

QR Code Scanning not working

So I have just taken over an iOS project as their first in-house dev, where previously this app had been developed by an agency.
One of the features of the app is that it needs to scan QR codes—by the look of the code the previous developers have followed this tutorial on AppCoda to implement the QR scan. Everything looks fine and I can't see anything wrong with the code yet it isn't working.
I also downloaded the completed tutorial project and that worked when I tried the QR scan. I also tried copying and pasting every single line so that it was identical to the working tutorial yet no luck.
I'm tearing my hair out trying to figure out why it isn't working.
Any help is much appreciated!
enum ScanState : Int {
case newDevice = 1
case resetDevice = 2
case replaceDevice = 3
}
class QRScannerViewController: BaseViewController,AVCaptureMetadataOutputObjectsDelegate {
#IBOutlet var scanZoneView: UIView!
#IBOutlet var scannerVIew: UIView!
#IBOutlet var scanInfoLabel: UILabel!
var capturedQR: String? = nil
var captureSession:AVCaptureSession?
var videoPreviewLayer:AVCaptureVideoPreviewLayer?
var qrCodeFrameView:UIView?
let supportedBarCodes = [AVMetadataObject.ObjectType.qr, AVMetadataObject.ObjectType.code128, AVMetadataObject.ObjectType.code39, AVMetadataObject.ObjectType.code93, AVMetadataObject.ObjectType.upce, AVMetadataObject.ObjectType.pdf417, AVMetadataObject.ObjectType.ean13, AVMetadataObject.ObjectType.aztec]
var type = "leg scan"
var device:Device?
var state:ScanState = .newDevice
override func viewDidLoad() {
super.viewDidLoad()
scanInfoLabel.text = "Scan your existing\nleg QR code"
self.navigationController?.navigationBar.dark()
//self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .Plain, target: nil, action: nil)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: true)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
#if NOHARDWARE
moveToNextViewController()
#else
initiateCapture()
#endif
}
func initiateCapture() {
let captureDevice = AVCaptureDevice.default(for: AVMediaType.video)
// Get an instance of the AVCaptureDeviceInput class using the previous device object.
var error:NSError?
let input: AnyObject!
do {
input = try AVCaptureDeviceInput(device: captureDevice!) as AVCaptureDeviceInput
} catch let error1 as NSError {
error = error1
input = nil
} catch _ {
input = nil
}
if (error != nil) {
// If any error occurs, simply log the description of it and don't continue any more.
print("\(error?.localizedDescription)")
return
}
// Initialize the captureSession object.
captureSession = AVCaptureSession()
// Set the input device on the capture session.
captureSession?.addInput(input as! AVCaptureInput)
// Initialize a AVCaptureMetadataOutput object and set it as the output device to the capture session.
let captureMetadataOutput = AVCaptureMetadataOutput()
captureSession?.addOutput(captureMetadataOutput)
// Set delegate and use the default dispatch queue to execute the call back
captureMetadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
captureMetadataOutput.metadataObjectTypes = supportedBarCodes
// Initialize the video preview layer and add it as a sublayer to the viewPreview view's layer.
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession!)
videoPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
videoPreviewLayer?.frame = scannerVIew.layer.bounds
scannerVIew.layer.addSublayer(videoPreviewLayer!)
// Start video capture.
captureSession?.startRunning()
// Initialize QR Code Frame to highlight the QR code
qrCodeFrameView = UIView()
qrCodeFrameView?.layer.borderColor = UIColor.green.cgColor
qrCodeFrameView?.layer.borderWidth = 2
scannerVIew.addSubview(qrCodeFrameView!)
scannerVIew.bringSubview(toFront: qrCodeFrameView!)
//qrCapturedLabel.text = "No QR code is detected"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func metadataOutput(captureOutput: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
// Check if the metadataObjects array is not nil and it contains at least one object.
if metadataObjects == nil || metadataObjects.count == 0 {
qrCodeFrameView?.frame = CGRect.zero
return
}
// Get the metadata object.
let metadataObj = metadataObjects[0] as! AVMetadataMachineReadableCodeObject
// Here we use filter method to check if the type of metadataObj is supported
// Instead of hardcoding the AVMetadataObjectTypeQRCode, we check if the type
// can be found in the array of supported bar codes.
if supportedBarCodes.filter({ $0 == metadataObj.type }).count > 0 {
// If the found metadata is equal to the QR code metadata then update the status label's text and set the bounds
let barCodeObject = videoPreviewLayer?.transformedMetadataObject(for: metadataObj as AVMetadataMachineReadableCodeObject) as! AVMetadataMachineReadableCodeObject
let intersectionRect = barCodeObject.bounds.intersection(self.scanZoneView.frame)
if !intersectionRect.isNull &&
(intersectionRect.size.width * intersectionRect.size.height) > self.scanZoneView.bounds.width * self.scanZoneView.bounds.height / 7 {
qrCodeFrameView?.frame = barCodeObject.bounds
if process(metadataObj.stringValue!) {
captureSession?.stopRunning()
}
}
}
}
#IBAction func didTapCancel(_ sender: AnyObject) {
self.dismiss(animated: true, completion: nil)
}
}
extension QRScannerViewController {
func process(_ scanText : String) -> Bool {
var legCode : String
let codeComponents = scanText.components(separatedBy: ";")
if codeComponents.count > 0 {
legCode = codeComponents[0]
} else {
// Invalid number of parameters seperated by a ;
return false
}
// TODO Validate the LEG to LEG-XXXXX
if legCode.hasPrefix("LEG-") {
let delta: Int64 = 1 * Int64(NSEC_PER_SEC)
let time = DispatchTime.now() + Double(delta) / Double(NSEC_PER_SEC)
DispatchQueue.main.asyncAfter(deadline: time, execute: {
switch self.state {
case .resetDevice:
let realm = try! Realm()
let deviceList = realm.objects(Device.self)
let lc = legCode
self.device = deviceList.filter("legCode = %#", lc).first
if self.device == nil {
// TODO Error message: Device not associated with LEG
let vc = ErrorViewController.createErrorViewController(.DeviceNotFound)
self.present(vc, animated: true, completion: nil)
return
}
self.moveToNextViewController()
default:
self.presentingViewController?.dismiss(animated: true, completion: nil)
}
});
return true
}
return false
}
func moveToNextViewController() {
let inspectionStoryboard = UIStoryboard(name: "Impact", bundle: nil)
if let resetVC = inspectionStoryboard.instantiateViewController(withIdentifier: ImpactDetectionViewController.storyboardID) as? ImpactDetectionViewController {
resetVC.device = device
// TODO Pass the impact type across too when the G2 API is set
self.navigationController?.pushViewController(resetVC, animated: false)
}
}
#IBAction func cancelToVC(_ segue: UIStoryboardSegue) { }
}
EDIT
By not working I mean the delegate for AVCaptureMetadataOutputObjectsDelegate is never being called so it never seems to be detecting a QR code. In the AppCoda tutorial it overlays a green square where it detects the QR code but this never happens when I put that code into this app.
The camera is actually running but QR codes are never deteced.
Turns out the answer was deceptively (and annoyingly) simple! Of course in Swift 4 the delegates have been renamed slightly. To fix all I had to was change
func metadataOutput(captureOutput: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
to
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
You're not connecting your class to AVCaptureMetadataOutput's delegate, that's why the AVCaptureMetadataOutputObjectsDelegate functions are not being invoked. More info: https://developer.apple.com/documentation/avfoundation/avcapturemetadataoutputobjectsdelegate
Try this:
// Initialize a AVCaptureMetadataOutput object and set it as the output device to the capture session.
let captureMetadataOutput = AVCaptureMetadataOutput()
captureMetadataOutput.metadataObjectsDelegate = self // assign self to the delegate.
captureSession?.addOutput(captureMetadataOutput)

Trying to move a variable to from one class to another

I am currently trying to move one variable from one class to another. I am using the AVFramework to read QR Codes. The QR Code ultimately reads to a string variable and then the string variable reads to a label.text. I would like to use this exact same text within the textview of another class.
QRScannerController houses the QR code while HistoryViewis where I'd like to re-use the variable. The problem is that once I scan the QR code and move to the History view, it reads nothing. Here is what I have so far. Below is the QRScannerController
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
if segue.destination .isKind(of:HistoryView.self){
let vc2 = segue.destination as! HistoryView
vc2.previousViewController = self
}
}
#IBOutlet var messageLabel:UILabel!
#IBOutlet var topbar: UIView!
#IBOutlet var segmentedControl: UISegmentedControl!
//#IBOutlet var textFacts: UITextView!
var captureSession:AVCaptureSession?
var videoPreviewLayer:AVCaptureVideoPreviewLayer?
var qrCodeFrameView:UIView?
let supportedCodeTypes = [AVMetadataObjectTypeUPCECode,
AVMetadataObjectTypeCode39Code,
AVMetadataObjectTypeCode39Mod43Code,
AVMetadataObjectTypeCode93Code,
AVMetadataObjectTypeCode128Code,
AVMetadataObjectTypeEAN8Code,
AVMetadataObjectTypeEAN13Code,
AVMetadataObjectTypeAztecCode,
AVMetadataObjectTypePDF417Code,
AVMetadataObjectTypeQRCode]
#IBAction func unwindToHomeScreen(segue: UIStoryboardSegue) {
dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// Get an instance of the AVCaptureDevice class to initialize a device object and provide the video as the media type parameter.
let captureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
do {
// Get an instance of the AVCaptureDeviceInput class using the previous device object.
let input = try AVCaptureDeviceInput(device: captureDevice)
// Initialize the captureSession object.
captureSession = AVCaptureSession()
// Set the input device on the capture session.
captureSession?.addInput(input)
// Initialize a AVCaptureMetadataOutput object and set it as the output device to the capture session.
let captureMetadataOutput = AVCaptureMetadataOutput()
captureSession?.addOutput(captureMetadataOutput)
// Set delegate and use the default dispatch queue to execute the call back
captureMetadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
captureMetadataOutput.metadataObjectTypes = supportedCodeTypes
// Initialize the video preview layer and add it as a sublayer to the viewPreview view's layer.
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
videoPreviewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
videoPreviewLayer?.frame = view.layer.bounds
view.layer.addSublayer(videoPreviewLayer!)
// Start video capture.
captureSession?.startRunning()
// Move the message label and top bar to the front
view.bringSubview(toFront: messageLabel)
view.bringSubview(toFront: topbar)
// Initialize QR Code Frame to highlight the QR code
qrCodeFrameView = UIView()
if let qrCodeFrameView = qrCodeFrameView {
qrCodeFrameView.layer.borderColor = UIColor.green.cgColor
qrCodeFrameView.layer.borderWidth = 2
view.addSubview(qrCodeFrameView)
view.bringSubview(toFront: qrCodeFrameView)
}
} catch {
// If any error occurs, simply print it out and don't continue any more.
print(error)
return
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - AVCaptureMetadataOutputObjectsDelegate Methods
public func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
// Check if the metadataObjects array is not nil and it contains at least one object.
if metadataObjects == nil || metadataObjects.count == 0 {
qrCodeFrameView?.frame = CGRect.zero
//messageLabel.text = "No QR/barcode is detected"
return
}
// Get the metadata object.
let metadataObj = metadataObjects[0] as! AVMetadataMachineReadableCodeObject
if supportedCodeTypes.contains(metadataObj.type) {
// If the found metadata is equal to the QR code metadata then update the status label's text and set the bounds
let barCodeObject = videoPreviewLayer?.transformedMetadataObject(for: metadataObj)
qrCodeFrameView?.frame = barCodeObject!.bounds
if metadataObj.stringValue != nil {
//captureSession?.stopRunning()
messageLabel.text = metadataObj.stringValue
messageLabel.text = messageLabel.text
//let vc: UINavigationController = storyboard?.instantiateViewController(withIdentifier: "View") as! UINavigationController
let vc3: UIViewController = storyboard!.instantiateViewController(withIdentifier: "View") //as! UIViewController
self.present(vc3, animated: true, completion: nil)
}
}
}
func transferViewControllerVariables() -> (UILabel){
return messageLabel
}
}
Here is the History View:
import UIKit
import AVFoundation
class HistoryView: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
#IBOutlet var textHistory:UITextView!
var previousViewController: QRScannerController?
#IBAction func unwindToHomeScreen(segue: UIStoryboardSegue) {
dismiss(animated: true, completion: nil)
}
#IBAction func toMaps(segue: UIStoryboardSegue) {
let vc3: UIViewController = storyboard!.instantiateViewController(withIdentifier: "front") //as! UIViewController
self.present(vc3, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let messageLabel = previousViewController?.transferViewControllerVariables()
//print(messageLabel.text as Any)
textHistory.text = messageLabel?.text
}
// Do any additional setup after loading the view.
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I would propose you just pass the actual text to the next view controller instead of the whole previous view controller.
The general problem is though that the prepareForSegue function will never be called, as the HistoryView controller is not presented via a segue, but programmatically.
To pass data to the HistoryView controller programmatically do this in the captureOutput function in the QRScannerViewController:
let vc3: HistoryView = storyboard!.instantiateViewController(withIdentifier: "View") as! HistoryView
vc3.messagetext = metadataObj.stringValue
self.present(vc3, animated: true, completion: nil)
To add a new property in the history vc:
class HistoryView: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
var messageText: String?
}
Then you can remove the prepareForSegue function completely as it is not called anyway.
You should declare a messageLabel in HistoryView. when performing segue, assign the value and that's it.
class HistoryView: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
var messageLabel: String?
}
class QRCodeScanner: UIViewController {
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
if segue.destination.isKind(of:HistoryView.self){
if let vc2 = segue.destination as? HistoryView {
vc2.messageLabel = self.messageLabel.text
}
}
}
}

Resources