Fix camera issues: front flash reset, rotation, Center Stage
Fixes: 1. Front flash now properly resets after capture - restorePreviewAfterFlash() called in photo delegate - Preview no longer stays white after taking photo 2. Device rotation support - updateVideoOrientation() updates capture connections - Uses modern videoRotationAngle API (iOS 17+) - Listens to UIDevice.orientationDidChangeNotification 3. Center Stage support for supported devices - Detects Center Stage availability on front camera - Toggle button in top control bar (person.crop.rectangle icon) - Yellow highlight when enabled - Updates availability on camera switch
This commit is contained in:
parent
9066635a4d
commit
c442acf464
@ -32,6 +32,12 @@ class CameraViewModel: NSObject {
|
||||
/// Toast message to display
|
||||
var toastMessage: String?
|
||||
|
||||
/// Whether Center Stage is available on this device
|
||||
var isCenterStageAvailable = false
|
||||
|
||||
/// Whether Center Stage is currently enabled
|
||||
var isCenterStageEnabled = false
|
||||
|
||||
let settings = SettingsViewModel() // Shared config
|
||||
|
||||
// MARK: - Screen Brightness Handling
|
||||
@ -90,12 +96,74 @@ class CameraViewModel: NSObject {
|
||||
session.commitConfiguration()
|
||||
session.startRunning()
|
||||
|
||||
// Check Center Stage availability
|
||||
updateCenterStageAvailability()
|
||||
|
||||
UIApplication.shared.isIdleTimerDisabled = true
|
||||
saveCurrentBrightness()
|
||||
// Set screen to full brightness for best ring light effect
|
||||
setBrightness(1.0)
|
||||
}
|
||||
|
||||
// MARK: - Center Stage
|
||||
|
||||
/// Updates Center Stage availability based on current camera
|
||||
private func updateCenterStageAvailability() {
|
||||
isCenterStageAvailable = AVCaptureDevice.isCenterStageEnabled || checkCenterStageSupport()
|
||||
isCenterStageEnabled = AVCaptureDevice.isCenterStageEnabled
|
||||
}
|
||||
|
||||
/// Checks if the current device supports Center Stage
|
||||
private func checkCenterStageSupport() -> Bool {
|
||||
guard let device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front) else {
|
||||
return false
|
||||
}
|
||||
// Center Stage is available if the device has it as an active format feature
|
||||
return device.activeFormat.isCenterStageSupported
|
||||
}
|
||||
|
||||
/// Toggles Center Stage on/off
|
||||
func toggleCenterStage() {
|
||||
guard isCenterStageAvailable else { return }
|
||||
|
||||
AVCaptureDevice.centerStageControlMode = .app
|
||||
AVCaptureDevice.isCenterStageEnabled.toggle()
|
||||
isCenterStageEnabled = AVCaptureDevice.isCenterStageEnabled
|
||||
}
|
||||
|
||||
// MARK: - Orientation
|
||||
|
||||
/// Updates video orientation based on device orientation
|
||||
func updateVideoOrientation(for orientation: UIDeviceOrientation) {
|
||||
guard let connection = photoOutput?.connection(with: .video) else { return }
|
||||
|
||||
// Calculate rotation angle (in degrees)
|
||||
let rotationAngle: CGFloat
|
||||
switch orientation {
|
||||
case .portrait:
|
||||
rotationAngle = 90
|
||||
case .portraitUpsideDown:
|
||||
rotationAngle = 270
|
||||
case .landscapeLeft:
|
||||
rotationAngle = 0
|
||||
case .landscapeRight:
|
||||
rotationAngle = 180
|
||||
default:
|
||||
rotationAngle = 90 // Default to portrait
|
||||
}
|
||||
|
||||
// Use modern rotation angle API (iOS 17+)
|
||||
if connection.isVideoRotationAngleSupported(rotationAngle) {
|
||||
connection.videoRotationAngle = rotationAngle
|
||||
}
|
||||
|
||||
// Also update video output connection
|
||||
if let videoConnection = videoOutput?.connection(with: .video),
|
||||
videoConnection.isVideoRotationAngleSupported(rotationAngle) {
|
||||
videoConnection.videoRotationAngle = rotationAngle
|
||||
}
|
||||
}
|
||||
|
||||
func switchCamera() {
|
||||
guard let session = captureSession else { return }
|
||||
session.beginConfiguration()
|
||||
@ -109,6 +177,9 @@ class CameraViewModel: NSObject {
|
||||
session.addInput(input)
|
||||
}
|
||||
session.commitConfiguration()
|
||||
|
||||
// Update Center Stage availability (only works on front camera)
|
||||
updateCenterStageAvailability()
|
||||
}
|
||||
|
||||
func capturePhoto() {
|
||||
@ -131,13 +202,14 @@ class CameraViewModel: NSObject {
|
||||
|
||||
let captureSettings = AVCapturePhotoSettings()
|
||||
photoOutput?.capturePhoto(with: captureSettings, delegate: self)
|
||||
|
||||
// Restore preview after capture completes
|
||||
try? await Task.sleep(for: .milliseconds(200))
|
||||
isPreviewHidden = false
|
||||
}
|
||||
}
|
||||
|
||||
/// Restores the preview after front flash capture
|
||||
func restorePreviewAfterFlash() {
|
||||
isPreviewHidden = false
|
||||
}
|
||||
|
||||
func startRecording() {
|
||||
guard let videoOutput = videoOutput, !isRecording else { return }
|
||||
let url = FileManager.default.temporaryDirectory.appendingPathComponent("video.mov")
|
||||
@ -224,6 +296,9 @@ extension CameraViewModel: AVCapturePhotoCaptureDelegate {
|
||||
let image = UIImage(data: data) else { return }
|
||||
|
||||
Task { @MainActor in
|
||||
// Restore preview first (in case front flash was used)
|
||||
restorePreviewAfterFlash()
|
||||
|
||||
// Store the captured image for preview
|
||||
capturedMedia = .photo(image)
|
||||
|
||||
|
||||
@ -59,6 +59,9 @@ struct ContentView: View {
|
||||
.onDisappear {
|
||||
viewModel.restoreBrightness()
|
||||
}
|
||||
.onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
|
||||
viewModel.updateVideoOrientation(for: UIDevice.current.orientation)
|
||||
}
|
||||
.sheet(isPresented: $showPaywall) {
|
||||
ProPaywallView()
|
||||
}
|
||||
@ -163,6 +166,22 @@ struct ContentView: View {
|
||||
|
||||
private var topControlBar: some View {
|
||||
HStack {
|
||||
// Center Stage button (only shown when available)
|
||||
if viewModel.isCenterStageAvailable {
|
||||
Button {
|
||||
viewModel.toggleCenterStage()
|
||||
} label: {
|
||||
Image(systemName: viewModel.isCenterStageEnabled ? "person.crop.rectangle.fill" : "person.crop.rectangle")
|
||||
.font(.body)
|
||||
.foregroundStyle(viewModel.isCenterStageEnabled ? .yellow : .white)
|
||||
.padding(Design.Spacing.small)
|
||||
.background(.ultraThinMaterial, in: .circle)
|
||||
}
|
||||
.accessibilityLabel(String(localized: "Center Stage"))
|
||||
.accessibilityValue(viewModel.isCenterStageEnabled ? "On" : "Off")
|
||||
.accessibilityHint(String(localized: "Keeps you centered in frame"))
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
// Grid toggle
|
||||
|
||||
Loading…
Reference in New Issue
Block a user