Stack Overflow Asked on December 27, 2021
I have a QR Code scanner, which is embedded in a UIViewController. I’ve been looking around at other posts and blogs but all are single page apps which present the UIViewController as the primary one on launch of the application. I just need it to open inside of a button action, and for some reason I can’t get it to present the scanner correctly?
Here’s the QR Scanner Code:
class ScannerViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
var captureSession: AVCaptureSession!
var previewLayer: AVCaptureVideoPreviewLayer!
override func viewDidLoad() {
super.viewDidLoad()
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 = [.qr]
} 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()
}
}
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)
}
func found(code: String) {
print(code)
}
override var prefersStatusBarHidden: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .portrait
}
}
and here’s my (very simplistic) attempt to have it open upon pressing a button:
struct ScanView: View{
var body: some View{
VStack {
Button(action: {
ScannerViewController()
}) {
Text("Scan QR Code").foregroundColor(Color.white).padding()
}.frame(width: 150)
.background(Color.blue)
.cornerRadius(25)
}
}
}
I’ve tried looking at a few other StackOverflow posts regarding these types of issues, but some are either using deprecated ways of scanning qr codes or are otherwise irrelevant to the issue I’m facing.
I’m sure it’s something fairly simple but my Swift/Xcode expertise mostly regards design and styling with basic knowledge of API calls, but I don’t necessarily understand how the different view hierarchies and calling methods work.
I would like to fix this issue, of course, but I’d also like to know what the issue was so that I can know for next time.
We can not directly call UIViewController
in SwiftUI
.
Need UIViewControllerRepresentable
to present a UIViewController
.
You can read about it here:
Try this, it is working and I can see the content through camera :
import SwiftUI
import UIKit
import AVFoundation
struct ScannerView: UIViewControllerRepresentable {
typealias UIViewControllerType = ScannerViewController
func updateUIViewController(_ uiViewController: ScannerViewController, context: Context) {
print("update")
}
func makeUIViewController(context: Context) -> ScannerViewController {
return ScannerViewController()
}
}
and in you struct
ScanView:
struct ContentView: View {
@State private var showingScanner = false
var body: some View {
VStack {
Button(action: {
self.showingScanner = true
}) {
Text("Scan QR Code").foregroundColor(Color.white).padding()
}.frame(width: 150)
.background(Color.blue)
.cornerRadius(25)
}.sheet(isPresented: $showingScanner) {
ScannerView()
}
}
}
The camera view, I have dragged the camera view below so that you can see the button screen in the background:
P.S. Don't forget to add Privacy - Camera Usage Description
in info.plist
Answered by Amit on December 27, 2021
I'm not an xCode expert. Much less a SwiftUI expert. But it looks like you're trying to navigate from a SwiftUI view to a UIKit view. That threw me off for a minute.
Anyways, you're going to want to wrap your button in a NavigationView. https://www.hackingwithswift.com/articles/216/complete-guide-to-navigationview-in-swiftui
Answered by StonedStudio on December 27, 2021
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP