From b97590f1c11a2c26e01778f1b177d3213ee84331 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 2 Jan 2026 16:19:46 -0600 Subject: [PATCH] Add Center Stage support alongside MijickCamera Center Stage is a device-level Apple feature that works independently of any camera framework. Added: - Center Stage button in top control bar (person.crop.rectangle icon) - checkCenterStageAvailability() checks device.activeFormat.isCenterStageSupported - toggleCenterStage() uses AVCaptureDevice.isCenterStageEnabled - Button only appears on devices that support Center Stage - Yellow highlight when enabled Works with MijickCamera since Center Stage is controlled at the AVCaptureDevice level, not the camera view level. --- .../Features/Camera/ContentView.swift | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/SelfieRingLight/Features/Camera/ContentView.swift b/SelfieRingLight/Features/Camera/ContentView.swift index a055a0b..edec368 100644 --- a/SelfieRingLight/Features/Camera/ContentView.swift +++ b/SelfieRingLight/Features/Camera/ContentView.swift @@ -2,6 +2,7 @@ import SwiftUI import MijickCamera import Bedrock import Photos +import AVFoundation struct ContentView: View { @State private var settings = SettingsViewModel() @@ -15,6 +16,10 @@ struct ContentView: View { @State private var capturedVideoURL: URL? @State private var showPostCapturePreview = false + // Center Stage support + @State private var isCenterStageAvailable = false + @State private var isCenterStageEnabled = false + var body: some View { GeometryReader { geometry in let maxRingSize = calculateMaxRingSize(for: geometry) @@ -65,6 +70,9 @@ struct ContentView: View { } } .ignoresSafeArea() + .onAppear { + checkCenterStageAvailability() + } .sheet(isPresented: $showPaywall) { ProPaywallView() } @@ -86,6 +94,22 @@ struct ContentView: View { private var topControlBar: some View { HStack { + // Center Stage button (only shown when available) + if isCenterStageAvailable { + Button { + toggleCenterStage() + } label: { + Image(systemName: isCenterStageEnabled ? "person.crop.rectangle.fill" : "person.crop.rectangle") + .font(.body) + .foregroundStyle(isCenterStageEnabled ? .yellow : .white) + .padding(Design.Spacing.small) + .background(.ultraThinMaterial, in: Circle()) + } + .accessibilityLabel(String(localized: "Center Stage")) + .accessibilityValue(isCenterStageEnabled ? "On" : "Off") + .accessibilityHint(String(localized: "Keeps you centered in frame")) + } + Spacer() // Grid toggle @@ -115,6 +139,28 @@ struct ContentView: View { } } + // MARK: - Center Stage + + /// Checks if Center Stage is available on this device + private func checkCenterStageAvailability() { + // Center Stage requires a compatible device (iPad with A12+ or some iPhones) + guard let device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front) else { + isCenterStageAvailable = false + return + } + + // Check if the device supports Center Stage + isCenterStageAvailable = device.activeFormat.isCenterStageSupported + isCenterStageEnabled = AVCaptureDevice.isCenterStageEnabled + } + + /// Toggles Center Stage on/off + private func toggleCenterStage() { + AVCaptureDevice.centerStageControlMode = .app + AVCaptureDevice.isCenterStageEnabled.toggle() + isCenterStageEnabled = AVCaptureDevice.isCenterStageEnabled + } + // MARK: - Post Capture View @ViewBuilder