From 1eaa3c19c42b44087940c292f2135c7685c24ba1 Mon Sep 17 00:00:00 2001 From: Oleksandr Skrypnyk Date: Wed, 23 Jul 2025 11:28:04 -0700 Subject: [PATCH] fix iPad orientation bug --- .../WeScan/Review/ReviewViewController.swift | 20 ++++++--- .../WeScan/Scan/CaptureSessionManager.swift | 26 ++++++++---- .../WeScan/Scan/ScannerViewController.swift | 41 +++++++++++++++++-- 3 files changed, 68 insertions(+), 19 deletions(-) diff --git a/Sources/WeScan/Review/ReviewViewController.swift b/Sources/WeScan/Review/ReviewViewController.swift index 4608c23e..469b3361 100644 --- a/Sources/WeScan/Review/ReviewViewController.swift +++ b/Sources/WeScan/Review/ReviewViewController.swift @@ -34,16 +34,12 @@ final class ReviewViewController: UIViewController { in: Bundle(for: ScannerViewController.self), compatibleWith: nil ) - let button = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(toggleEnhancedImage)) - button.tintColor = .white - return button + return UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(toggleEnhancedImage)) }() private lazy var rotateButton: UIBarButtonItem = { let image = UIImage(systemName: "rotate.right", named: "rotate", in: Bundle(for: ScannerViewController.self), compatibleWith: nil) - let button = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(rotateImage)) - button.tintColor = .white - return button + return UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(rotateImage)) }() private lazy var doneButton: UIBarButtonItem = { @@ -136,6 +132,14 @@ final class ReviewViewController: UIViewController { NSLayoutConstraint.activate(imageViewConstraints) } + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + + let isDark = traitCollection.userInterfaceStyle == .dark + enhanceButton.tintColor = isDark ? .white : .black + rotateButton.tintColor = isDark ? .white : .black + } + // MARK: - Actions @objc private func reloadImage() { @@ -176,6 +180,10 @@ final class ReviewViewController: UIViewController { newResults.croppedScan.rotate(by: rotationAngle) newResults.enhancedScan?.rotate(by: rotationAngle) newResults.doesUserPreferEnhancedScan = isCurrentlyDisplayingEnhancedImage + + if isCurrentlyDisplayingEnhancedImage, let enhancedScan = newResults.enhancedScan { + newResults.croppedScan = enhancedScan + } imageScannerController.imageScannerDelegate? .imageScannerController(imageScannerController, didFinishScanningWithResults: newResults) } diff --git a/Sources/WeScan/Scan/CaptureSessionManager.swift b/Sources/WeScan/Scan/CaptureSessionManager.swift index 57df7c5d..191e322a 100644 --- a/Sources/WeScan/Scan/CaptureSessionManager.swift +++ b/Sources/WeScan/Scan/CaptureSessionManager.swift @@ -322,6 +322,8 @@ extension CaptureSessionManager: AVCapturePhotoCaptureDelegate { /// Completes the image capture by processing the image, and passing it to the delegate object. /// This function is necessary because the capture functions for iOS 10 and 11 are decoupled. private func completeImageCapture(with imageData: Data) { + let interfaceOrientation = (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.interfaceOrientation ?? .portrait + DispatchQueue.global(qos: .background).async { [weak self] in CaptureSession.current.isEditing = true guard let image = UIImage(data: imageData) else { @@ -335,13 +337,19 @@ extension CaptureSessionManager: AVCapturePhotoCaptureDelegate { return } - var angle: CGFloat = 0.0 - - switch image.imageOrientation { - case .right: - angle = CGFloat.pi / 2 - case .up: - angle = CGFloat.pi + var rotatedImage = image + var angle: CGFloat = .pi / 2 + + switch interfaceOrientation { + case .landscapeRight: + rotatedImage = UIImage(cgImage: image.cgImage!, scale: 1.0, orientation: .down) + angle = 0 + case .landscapeLeft: + rotatedImage = UIImage(cgImage: image.cgImage!, scale: 1.0, orientation: .up) + angle = .pi + case .portraitUpsideDown: + rotatedImage = UIImage(cgImage: image.cgImage!, scale: 1.0, orientation: .left) + angle = -.pi / 2 default: break } @@ -349,14 +357,14 @@ extension CaptureSessionManager: AVCapturePhotoCaptureDelegate { var quad: Quadrilateral? if let displayedRectangleResult = self?.displayedRectangleResult { quad = self?.displayRectangleResult(rectangleResult: displayedRectangleResult) - quad = quad?.scale(displayedRectangleResult.imageSize, image.size, withRotationAngle: angle) + quad = quad?.scale(displayedRectangleResult.imageSize, rotatedImage.size, withRotationAngle: angle) } DispatchQueue.main.async { guard let self else { return } - self.delegate?.captureSessionManager(self, didCapturePicture: image, withQuad: quad) + self.delegate?.captureSessionManager(self, didCapturePicture: rotatedImage, withQuad: quad) } } } diff --git a/Sources/WeScan/Scan/ScannerViewController.swift b/Sources/WeScan/Scan/ScannerViewController.swift index 50c10765..cf4c23bd 100644 --- a/Sources/WeScan/Scan/ScannerViewController.swift +++ b/Sources/WeScan/Scan/ScannerViewController.swift @@ -46,7 +46,7 @@ public final class ScannerViewController: UIViewController { private lazy var autoScanButton: UIBarButtonItem = { let title = NSLocalizedString("wescan.scanning.auto", tableName: nil, bundle: Bundle(for: ScannerViewController.self), value: "Auto", comment: "The auto button state") let button = UIBarButtonItem(title: title, style: .plain, target: self, action: #selector(toggleAutoScan)) - button.tintColor = .white + button.tintColor = navigationController?.navigationBar.tintColor return button }() @@ -54,7 +54,7 @@ public final class ScannerViewController: UIViewController { private lazy var flashButton: UIBarButtonItem = { let image = UIImage(systemName: "bolt.fill", named: "flash", in: Bundle(for: ScannerViewController.self), compatibleWith: nil) let button = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(toggleFlash)) - button.tintColor = .white + button.tintColor = navigationController?.navigationBar.tintColor return button }() @@ -100,6 +100,21 @@ public final class ScannerViewController: UIViewController { override public func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() + if let connection = videoPreviewLayer.connection, connection.isVideoOrientationSupported { + let interfaceOrientation = (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.interfaceOrientation ?? .portrait + + switch interfaceOrientation { + case .portraitUpsideDown: + connection.videoOrientation = .portraitUpsideDown + case .landscapeLeft: + connection.videoOrientation = .landscapeLeft + case .landscapeRight: + connection.videoOrientation = .landscapeRight + default: + connection.videoOrientation = .portrait + } + } + videoPreviewLayer.frame = view.layer.bounds } @@ -304,12 +319,30 @@ extension ScannerViewController: RectangleDetectionDelegateProtocol { return } - let portraitImageSize = CGSize(width: imageSize.height, height: imageSize.width) + let statusBarOrientation = UIApplication.shared.statusBarOrientation + + let rotationAngle: CGFloat + let portraitImageSize: CGSize + + switch statusBarOrientation { + case .portrait: + rotationAngle = .pi / 2 + portraitImageSize = CGSize(width: imageSize.height, height: imageSize.width) + case .landscapeLeft: + rotationAngle = .pi + portraitImageSize = imageSize + case .portraitUpsideDown: + rotationAngle = -.pi / 2 + portraitImageSize = CGSize(width: imageSize.height, height: imageSize.width) + @unknown default: + rotationAngle = 0 + portraitImageSize = imageSize + } let scaleTransform = CGAffineTransform.scaleTransform(forSize: portraitImageSize, aspectFillInSize: quadView.bounds.size) let scaledImageSize = imageSize.applying(scaleTransform) - let rotationTransform = CGAffineTransform(rotationAngle: CGFloat.pi / 2.0) + let rotationTransform = CGAffineTransform(rotationAngle: rotationAngle) let imageBounds = CGRect(origin: .zero, size: scaledImageSize).applying(rotationTransform)