|
@@ -2,20 +2,43 @@ import AVFoundation
|
|
|
import UIKit
|
|
|
|
|
|
class QrCodeReaderController: UIViewController {
|
|
|
- var captureSession = AVCaptureSession()
|
|
|
-
|
|
|
- var videoPreviewLayer: AVCaptureVideoPreviewLayer?
|
|
|
|
|
|
weak var delegate: QrCodeReaderDelegate?
|
|
|
|
|
|
+ private let captureSession = AVCaptureSession()
|
|
|
+
|
|
|
+ private lazy var videoPreviewLayer: AVCaptureVideoPreviewLayer = {
|
|
|
+ let videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
|
|
|
+ videoPreviewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
|
|
|
+ return videoPreviewLayer
|
|
|
+ }()
|
|
|
+
|
|
|
+ private var infoLabel: UILabel = {
|
|
|
+ let label = UILabel()
|
|
|
+ label.translatesAutoresizingMaskIntoConstraints = false
|
|
|
+ label.text = String.localized("qrscan_hint")
|
|
|
+ label.lineBreakMode = .byWordWrapping
|
|
|
+ label.numberOfLines = 0
|
|
|
+ label.textAlignment = .center
|
|
|
+ label.textColor = .white
|
|
|
+ return label
|
|
|
+ }()
|
|
|
+
|
|
|
+ private lazy var closeButton: UIBarButtonItem = {
|
|
|
+ return UIBarButtonItem(title: String.localized("cancel"), style: .done, target: self, action: #selector(closeButtonPressed(_:)))
|
|
|
+ }()
|
|
|
+
|
|
|
+
|
|
|
private let supportedCodeTypes = [
|
|
|
AVMetadataObject.ObjectType.qr
|
|
|
]
|
|
|
|
|
|
+ // MARK: - lifecycle
|
|
|
override func viewDidLoad() {
|
|
|
super.viewDidLoad()
|
|
|
self.edgesForExtendedLayout = []
|
|
|
title = String.localized("qrscan_title")
|
|
|
+ navigationItem.leftBarButtonItem = closeButton
|
|
|
|
|
|
guard let captureDevice = AVCaptureDevice.DiscoverySession.init(
|
|
|
deviceTypes: [AVCaptureDevice.DeviceType.builtInWideAngleCamera],
|
|
@@ -40,33 +63,23 @@ class QrCodeReaderController: UIViewController {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
|
|
|
- videoPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
|
|
|
- videoPreviewLayer?.frame = view.layer.bounds
|
|
|
- view.layer.addSublayer(videoPreviewLayer!)
|
|
|
-
|
|
|
- let infoLabel = createInfoLabel()
|
|
|
- view.addSubview(infoLabel)
|
|
|
- view.addConstraint(infoLabel.constraintAlignBottomTo(view, paddingBottom: 8))
|
|
|
- view.addConstraint(infoLabel.constraintCenterXTo(view))
|
|
|
- view.bringSubviewToFront(infoLabel)
|
|
|
+ setupSubviews()
|
|
|
}
|
|
|
|
|
|
- private func createInfoLabel() -> UIView {
|
|
|
- let label = UILabel()
|
|
|
- label.translatesAutoresizingMaskIntoConstraints = false
|
|
|
- label.text = String.localized("qrscan_hint")
|
|
|
- label.lineBreakMode = .byWordWrapping
|
|
|
- label.numberOfLines = 0
|
|
|
- label.textAlignment = .center
|
|
|
- label.textColor = .white
|
|
|
- return label
|
|
|
+ override func viewWillAppear(_ animated: Bool) {
|
|
|
+ captureSession.startRunning()
|
|
|
}
|
|
|
|
|
|
+ override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
|
|
+ super.viewWillTransition(to: size, with: coordinator)
|
|
|
|
|
|
- override func viewWillAppear(_ animated: Bool) {
|
|
|
- captureSession.startRunning()
|
|
|
+ coordinator.animate(alongsideTransition: nil, completion: { [weak self] _ in
|
|
|
+ DispatchQueue.main.async(execute: {
|
|
|
+ self?.updateVideoOrientation()
|
|
|
+ })
|
|
|
+ })
|
|
|
}
|
|
|
+
|
|
|
override func viewWillDisappear(_ animated: Bool) {
|
|
|
captureSession.stopRunning()
|
|
|
}
|
|
@@ -75,6 +88,45 @@ class QrCodeReaderController: UIViewController {
|
|
|
super.didReceiveMemoryWarning()
|
|
|
// Dispose of any resources that can be recreated.
|
|
|
}
|
|
|
+
|
|
|
+ // MARK: - setup
|
|
|
+ private func setupSubviews() {
|
|
|
+ view.layer.addSublayer(videoPreviewLayer)
|
|
|
+ videoPreviewLayer.frame = view.layer.bounds
|
|
|
+ view.addSubview(infoLabel)
|
|
|
+ infoLabel.translatesAutoresizingMaskIntoConstraints = false
|
|
|
+ view.addConstraint(infoLabel.constraintAlignBottomTo(view, paddingBottom: 8))
|
|
|
+ view.addConstraint(infoLabel.constraintCenterXTo(view))
|
|
|
+ view.bringSubviewToFront(infoLabel)
|
|
|
+ }
|
|
|
+
|
|
|
+ func updateVideoOrientation() {
|
|
|
+
|
|
|
+ guard let connection = videoPreviewLayer.connection else {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ guard connection.isVideoOrientationSupported else {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ let statusBarOrientation = UIApplication.shared.statusBarOrientation
|
|
|
+ let videoOrientation: AVCaptureVideoOrientation = statusBarOrientation.videoOrientation ?? .portrait
|
|
|
+
|
|
|
+ if connection.videoOrientation == videoOrientation {
|
|
|
+ print("no change to videoOrientation")
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ videoPreviewLayer.frame = view.bounds
|
|
|
+ connection.videoOrientation = videoOrientation
|
|
|
+ videoPreviewLayer.removeAllAnimations()
|
|
|
+ }
|
|
|
+
|
|
|
+ // MARK: - actions
|
|
|
+ @objc private func closeButtonPressed(_ sender: UIBarButtonItem) {
|
|
|
+ self.dismiss(animated: true, completion: nil)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
extension QrCodeReaderController: AVCaptureMetadataOutputObjectsDelegate {
|
|
@@ -89,3 +141,15 @@ extension QrCodeReaderController: AVCaptureMetadataOutputObjectsDelegate {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+extension UIInterfaceOrientation {
|
|
|
+ var videoOrientation: AVCaptureVideoOrientation? {
|
|
|
+ switch self {
|
|
|
+ case .portraitUpsideDown: return .portraitUpsideDown
|
|
|
+ case .landscapeRight: return .landscapeRight
|
|
|
+ case .landscapeLeft: return .landscapeLeft
|
|
|
+ case .portrait: return .portrait
|
|
|
+ default: return nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|