Reputation: 4914
I am using the code provided by https://www.hackingwithswift.com/example-code/media/how-to-scan-a-qr-code to make my own scanning app. But I like my scanning to occur on button press. Now for this I put the viewDidLoad()
part from the tutorial into its own function:
func cameraScanningLayer(){
view.backgroundColor = UIColor.blackColor()
captureSession = AVCaptureSession()
let videoCaptureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
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: dispatch_get_main_queue())
// need to scan barcode + QRcode
metadataOutput.metadataObjectTypes = [AVMetadataObjectTypeQRCode,AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypePDF417Code,AVMetadataObjectTypeCode128Code,AVMetadataObjectTypeCode39Code]
} else {
failed()
return
}
// Previewlayer with camera
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession);
previewLayer.frame = viewForLayer.bounds;
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
viewForLayer.layer.addSublayer(previewLayer);
captureSession.startRunning();
}
And a button action calls the function:
func buttonScanAction() {
print("Scan")
scanEnabled = true // like to use some kind of bool/switch
self.cameraScanningLayer()
}
The problems I have are:
1) On load the camera is not in view
2) After the button is pressed the camera is in view but it always scans automatically
So I thought of using a global:
var scanEnabled: Bool = false
Then, when the button is clicked, set it to true and the scanning is enabled.
For reference here is a sketch:
EDIT my quick fix which might not be the right way to do it.
I replaced the
let metadataOutput = AVCaptureMetadataOutput() {...} else {
failed()
return
}
and put it between an if statement
if (scanEnabled == true) {
let metadataOutput = AVCaptureMetadataOutput()
if (captureSession.canAddOutput(metadataOutput)) {
captureSession.addOutput(metadataOutput)
metadataOutput.setMetadataObjectsDelegate(self, queue: dispatch_get_main_queue())
// to use them both wwe need to skip AVMetadataObjectTypeQRCode
metadataOutput.metadataObjectTypes = [AVMetadataObjectTypeQRCode,AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypePDF417Code,AVMetadataObjectTypeCode128Code,AVMetadataObjectTypeCode39Code]
scanEnabled = false
} else {
failed()
return
}
}
Upvotes: 2
Views: 2776
Reputation: 4257
Thanks @alex for this question. I also use the excellent class created by twostraws (Very useful Paul, thank you so much) and also needed to implement code scan reading with only the action of a button. My solution was the following:
I define metadataOutput
as a global variable and only in the button action do I integrate them as a delegate:
var metadataOutput: AVCaptureMetadataOutput!
In viewDidLoad
method:
metadataOutput = AVCaptureMetadataOutput()
if (captureSession.canAddOutput(metadataOutput)) {
captureSession.addOutput(metadataOutput)
// Was removed this line: metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
metadataOutput.metadataObjectTypes = [.qr]
} else {
failed()
return
}
func buttonScanAction() {
print("Scan")
metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
}
When I change my view I stop the camera and remove the delegate like this:
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if (captureSession?.isRunning == true) {
captureSession.stopRunning()
}
metadataOutput.setMetadataObjectsDelegate(nil, queue: DispatchQueue.main)
}
Upvotes: 0
Reputation: 13127
Author of that tutorial here. My method was to use a dedicated scanning view controller, but I guess you want to unify that with your existing view controller – and that's fine. Both approaches work.
If you want to show the camera interface all the time (even when not actively recognising QR codes) then your plan to use a boolean to track whether scanning is enabled is a good one. My example code has a foundCode()
method that gets called, and also calls dismissViewControllerAnimated()
when codes are found.
In your version, you need to make foundCode()
do all the work of stopping the scane, handling the dismissal, etc. You can then add a check for your scanEnabled
boolean in one place.
Something like this ought to do it:
func foundCode(code: String) {
if scanCode == true {
print(code)
captureSession.stopRunning()
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
dismissViewControllerAnimated(true, completion: nil)
}
}
If you wanted to, you could move the scanCode == true
check up to didOutputMetadataObjects
to save the unnecessary method call.
Upvotes: 4