Automatically starts scanning the QR code if we have not received a QR code.

This commit is contained in:
Nicolas Mauri
2023-02-17 18:08:17 +01:00
parent 264c1f8c7c
commit c09c88fc6d
6 changed files with 363 additions and 180 deletions
@@ -15,7 +15,6 @@
*/
import UIKit
import ZXingObjC
protocol QRCodeReaderViewControllerDelegate: AnyObject {
func qrCodeReaderViewController(_ viewController: QRCodeReaderViewController, didFound payloadData: Data)
@@ -40,10 +39,7 @@ final class QRCodeReaderViewController: UIViewController {
private var theme: Theme!
private var errorPresenter: MXKErrorPresentation!
private lazy var zxCapture: ZXCapture = ZXCapture()
private var captureSizeTransform: CGAffineTransform?
private var isScanning: Bool = false
private var isFirstApplyOrientation: Bool = false
private var qrCodeReaderView: QRCodeReaderView!
// MARK: Public
@@ -56,12 +52,7 @@ final class QRCodeReaderViewController: UIViewController {
viewController.theme = ThemeService.shared().theme
return viewController
}
deinit {
self.zxCapture.layer.removeFromSuperlayer()
self.zxCapture.hard_stop()
}
// MARK: - Life cycle
override func viewDidLoad() {
@@ -92,40 +83,14 @@ final class QRCodeReaderViewController: UIViewController {
return self.theme.statusBarStyle
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
guard isFirstApplyOrientation == false else {
return
}
isFirstApplyOrientation = true
applyOrientation()
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { (context) in
// do nothing
}, completion: { [weak self] (context) in
guard let self = self else {
return
}
self.applyOrientation()
})
}
// MARK: - Public
func startScanning() {
self.zxCapture.start()
isScanning = true
qrCodeReaderView.startScanning()
}
func stopScanning() {
self.zxCapture.stop()
isScanning = false
qrCodeReaderView.stopScanning()
}
// MARK: - Private
@@ -145,94 +110,15 @@ final class QRCodeReaderViewController: UIViewController {
}
private func setupViews() {
self.setupQRCodeReaderView()
let qrCodeReaderView = QRCodeReaderView()
qrCodeReaderView.didFoundData = qrCodeReader(didFound:)
self.qrCodeReaderView = qrCodeReaderView
self.codeReaderContainerView.vc_addSubViewMatchingParent(qrCodeReaderView)
}
private func setupQRCodeReaderView() {
zxCapture.delegate = self
zxCapture.camera = zxCapture.back()
zxCapture.layer.frame = codeReaderContainerView.bounds
codeReaderContainerView.layer.addSublayer(zxCapture.layer)
}
private func applyOrientation() {
let orientation = UIApplication.shared.statusBarOrientation
let captureRotation: Double
let scanRectRotation: Double
switch orientation {
case .portrait:
captureRotation = 0
scanRectRotation = 90
case .landscapeLeft:
captureRotation = 90
scanRectRotation = 180
case .landscapeRight:
captureRotation = 270
scanRectRotation = 0
case .portraitUpsideDown:
captureRotation = 180
scanRectRotation = 270
default:
captureRotation = 0
scanRectRotation = 90
}
applyRectOfInterest(orientation: orientation)
let angleRadius = captureRotation / 180.0 * Double.pi
let captureTranform = CGAffineTransform(rotationAngle: CGFloat(angleRadius))
zxCapture.transform = captureTranform
zxCapture.rotation = CGFloat(scanRectRotation)
zxCapture.layer.frame = codeReaderContainerView.frame
}
private func applyRectOfInterest(orientation: UIInterfaceOrientation) {
guard var transformedVideoRect = codeReaderContainerView?.frame,
let cameraSessionPreset = zxCapture.sessionPreset
else { return }
var scaleVideoX, scaleVideoY: CGFloat
var videoHeight, videoWidth: CGFloat
// Currently support only for 1920x1080 || 1280x720
if cameraSessionPreset == AVCaptureSession.Preset.hd1920x1080.rawValue {
videoHeight = 1080.0
videoWidth = 1920.0
} else {
videoHeight = 720.0
videoWidth = 1280.0
}
if orientation == UIInterfaceOrientation.portrait {
scaleVideoX = self.view.frame.width / videoHeight
scaleVideoY = self.view.frame.height / videoWidth
// Convert CGPoint under portrait mode to map with orientation of image
// because the image will be cropped before rotate
// reference: https://github.com/TheLevelUp/ZXingObjC/issues/222
let realX = transformedVideoRect.origin.y
let realY = self.view.frame.size.width - transformedVideoRect.size.width - transformedVideoRect.origin.x
let realWidth = transformedVideoRect.size.height
let realHeight = transformedVideoRect.size.width
transformedVideoRect = CGRect(x: realX, y: realY, width: realWidth, height: realHeight)
} else {
scaleVideoX = self.view.frame.width / videoWidth
scaleVideoY = self.view.frame.height / videoHeight
}
captureSizeTransform = CGAffineTransform(scaleX: 1.0/scaleVideoX, y: 1.0/scaleVideoY)
guard let _captureSizeTransform = captureSizeTransform else {
return
}
let transformRect = transformedVideoRect.applying(_captureSizeTransform)
zxCapture.scanRect = transformRect
private func qrCodeReader(didFound data: Data) {
self.delegate?.qrCodeReaderViewController(self, didFound: data)
}
// MARK: - Actions
@@ -241,31 +127,3 @@ final class QRCodeReaderViewController: UIViewController {
self.delegate?.qrCodeReaderViewControllerDidCancel(self)
}
}
// MARK: - ZXCaptureDelegate
extension QRCodeReaderViewController: ZXCaptureDelegate {
func captureCameraIsReady(_ capture: ZXCapture!) {
isScanning = true
}
func captureResult(_ capture: ZXCapture!, result: ZXResult!) {
guard let zxResult = result, isScanning == true else {
return
}
guard zxResult.barcodeFormat == kBarcodeFormatQRCode else {
return
}
self.stopScanning()
if let bytes = result.resultMetadata.object(forKey: kResultMetadataTypeByteSegments.rawValue) as? NSArray,
let byteArray = bytes.firstObject as? ZXByteArray {
let data = Data(bytes: UnsafeRawPointer(byteArray.array), count: Int(byteArray.length))
self.delegate?.qrCodeReaderViewController(self, didFound: data)
}
}
}