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+)
This commit is contained in:
Matt Bruce 2026-01-02 16:00:03 -06:00
parent 0f2655593f
commit 80976c61d1
2 changed files with 56 additions and 4 deletions

View File

@ -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
}
}
}

View File

@ -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