From 80976c61d1f633c6b3d254c25309ace4aae51d94 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 2 Jan 2026 16:00:03 -0600 Subject: [PATCH] Fix camera preview not rotating with device orientation The preview layer's connection needs its own rotation update, separate from the capture output connections. Changes: - CameraPreviewUIView now listens to UIDevice.orientationDidChangeNotification - updatePreviewOrientation() updates the preview layer's connection - Called on layoutSubviews and orientation changes - Handles faceUp/faceDown/unknown by keeping current orientation - Uses modern videoRotationAngle API (iOS 17+) --- .../Features/Camera/CameraPreview.swift | 52 +++++++++++++++++++ .../Resources/Localizable.xcstrings | 8 +-- 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/SelfieRingLight/Features/Camera/CameraPreview.swift b/SelfieRingLight/Features/Camera/CameraPreview.swift index 4706772..a762fb6 100644 --- a/SelfieRingLight/Features/Camera/CameraPreview.swift +++ b/SelfieRingLight/Features/Camera/CameraPreview.swift @@ -104,12 +104,24 @@ class CameraPreviewUIView: UIView { backgroundColor = .black autoresizingMask = [.flexibleWidth, .flexibleHeight] setupPreviewLayer() + + // Listen for orientation changes + NotificationCenter.default.addObserver( + self, + selector: #selector(handleOrientationChange), + name: UIDevice.orientationDidChangeNotification, + object: nil + ) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + deinit { + NotificationCenter.default.removeObserver(self) + } + private func setupPreviewLayer() { guard let viewModel = viewModel, let session = viewModel.captureSession else { return } @@ -119,6 +131,9 @@ class CameraPreviewUIView: UIView { layer.videoGravity = .resizeAspectFill previewLayer = layer viewModel.previewLayer = layer + + // Set initial orientation + updatePreviewOrientation() } } @@ -131,5 +146,42 @@ class CameraPreviewUIView: UIView { if previewLayer == nil { setupPreviewLayer() } + + // Update orientation on layout changes + updatePreviewOrientation() + } + + @objc private func handleOrientationChange() { + updatePreviewOrientation() + } + + private func updatePreviewOrientation() { + guard let connection = previewLayer?.connection else { return } + + // Get rotation angle based on device orientation + let deviceOrientation = UIDevice.current.orientation + + // Calculate rotation angle (in degrees) for the preview layer + let rotationAngle: CGFloat + switch deviceOrientation { + case .portrait: + rotationAngle = 90 + case .portraitUpsideDown: + rotationAngle = 270 + case .landscapeLeft: + rotationAngle = 180 + case .landscapeRight: + rotationAngle = 0 + case .faceUp, .faceDown, .unknown: + // Keep current orientation for flat/unknown positions + return + @unknown default: + rotationAngle = 90 // Default to portrait + } + + // Use modern rotation angle API (iOS 17+) + if connection.isVideoRotationAngleSupported(rotationAngle) { + connection.videoRotationAngle = rotationAngle + } } } diff --git a/SelfieRingLight/Resources/Localizable.xcstrings b/SelfieRingLight/Resources/Localizable.xcstrings index c63ad7e..f25267f 100644 --- a/SelfieRingLight/Resources/Localizable.xcstrings +++ b/SelfieRingLight/Resources/Localizable.xcstrings @@ -160,6 +160,10 @@ "comment" : "A label displayed above a section of the settings view related to light colors.", "isCommentAutoGenerated" : true }, + "Locked. Tap to unlock with Pro." : { + "comment" : "A hint that appears when a user taps on a color preset button.", + "isCommentAutoGenerated" : true + }, "No Watermarks • Ad-Free" : { "comment" : "Description of a benefit that comes with the Pro subscription.", "isCommentAutoGenerated" : true @@ -176,10 +180,6 @@ "comment" : "A button label that opens the device settings when tapped.", "isCommentAutoGenerated" : true }, - "Opens color picker. Premium feature." : { - "comment" : "An accessibility hint for the custom color button, describing its function.", - "isCommentAutoGenerated" : true - }, "Opens upgrade options" : { "comment" : "An accessibility hint for the \"Upgrade to Pro\" button that indicates it opens upgrade options.", "isCommentAutoGenerated" : true