Camera works, provided you don't go back too rapidly

This commit is contained in:
2026-02-15 14:15:34 -06:00
parent 45035e67e0
commit 55b48575cc
5 changed files with 80 additions and 11 deletions

View File

@@ -26,9 +26,11 @@ final class CameraManager: NSObject, ObservableObject {
private let continuationQueue = DispatchQueue(label: "com.souschef.continuation")
private var isConfigured = false
private var cachedPreviewLayer: AVCaptureVideoPreviewLayer?
nonisolated override init() {
super.init()
print("🎥 CameraManager.init() - Instance created at \(Date())")
}
// MARK: - Authorization
@@ -53,8 +55,12 @@ final class CameraManager: NSObject, ObservableObject {
// MARK: - Session Setup
func setupSession() async throws {
print("🎥 CameraManager.setupSession() - STARTED at \(Date())")
// Only configure once
guard !isConfigured else { return }
guard !isConfigured else {
print("🎥 CameraManager.setupSession() - Already configured, returning")
return
}
// Ensure authorization is checked first
await checkAuthorization()
@@ -63,19 +69,29 @@ final class CameraManager: NSObject, ObservableObject {
throw CameraError.notAuthorized
}
print("🎥 CameraManager.setupSession() - Calling beginConfiguration()")
captureSession.beginConfiguration()
// Set session preset
captureSession.sessionPreset = .high
print("🎥 CameraManager.setupSession() - Set preset to .high")
// Add video input
print("🎥 CameraManager.setupSession() - About to get video device (LINE 72)")
#if targetEnvironment(simulator)
print("🎥 CameraManager.setupSession() - ⚠️ RUNNING ON SIMULATOR - Camera may not work properly")
#endif
guard let videoDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back),
let videoInput = try? AVCaptureDeviceInput(device: videoDevice),
captureSession.canAddInput(videoInput) else {
print("🎥 CameraManager.setupSession() - ❌ FAILED to get video device or add input")
captureSession.commitConfiguration()
throw CameraError.setupFailed
}
print("🎥 CameraManager.setupSession() - ✅ Successfully got video device and input")
captureSession.addInput(videoInput)
// Add video output
@@ -96,6 +112,7 @@ final class CameraManager: NSObject, ObservableObject {
captureSession.commitConfiguration()
isConfigured = true
print("🎥 CameraManager.setupSession() - ✅ COMPLETED successfully at \(Date())")
}
// MARK: - Session Control
@@ -151,9 +168,26 @@ final class CameraManager: NSObject, ObservableObject {
// MARK: - Preview Layer
func previewLayer() -> AVCaptureVideoPreviewLayer {
func previewLayer() -> AVCaptureVideoPreviewLayer? {
print("🎥 CameraManager.previewLayer() - ⚠️ CALLED at \(Date()) - isConfigured: \(isConfigured)")
// Only create preview layer after session is configured
if !isConfigured {
print("🎥 CameraManager.previewLayer() - ❌ Session not configured yet, returning nil")
return nil
}
// Return cached layer if available
if let cached = cachedPreviewLayer {
print("🎥 CameraManager.previewLayer() - ✅ Returning cached preview layer")
return cached
}
// Create and cache new preview layer
print("🎥 CameraManager.previewLayer() - ✅ Creating new preview layer")
let layer = AVCaptureVideoPreviewLayer(session: captureSession)
layer.videoGravity = .resizeAspectFill
cachedPreviewLayer = layer
return layer
}
}

View File

@@ -71,7 +71,7 @@ final class CookingModeViewModel: ObservableObject {
cameraManager.stopSession()
}
func getPreviewLayer() -> AVCaptureVideoPreviewLayer {
func getPreviewLayer() -> AVCaptureVideoPreviewLayer? {
cameraManager.previewLayer()
}

View File

@@ -25,6 +25,7 @@ final class ScannerViewModel: ObservableObject {
nonisolated init(visionService: VisionService = ARVisionService(),
cameraManager: CameraManager = CameraManager()) {
print("📱 ScannerViewModel.init() - Creating ViewModel at \(Date())")
self.visionService = visionService
self.cameraManager = cameraManager
}
@@ -32,9 +33,12 @@ final class ScannerViewModel: ObservableObject {
// MARK: - Camera Management
func setupCamera() async {
print("📱 ScannerViewModel.setupCamera() - STARTED at \(Date())")
do {
try await cameraManager.setupSession()
print("📱 ScannerViewModel.setupCamera() - ✅ SUCCESS at \(Date())")
} catch {
print("📱 ScannerViewModel.setupCamera() - ❌ ERROR: \(error)")
self.error = error
}
}
@@ -47,8 +51,9 @@ final class ScannerViewModel: ObservableObject {
cameraManager.stopSession()
}
func getPreviewLayer() -> AVCaptureVideoPreviewLayer {
cameraManager.previewLayer()
func getPreviewLayer() -> AVCaptureVideoPreviewLayer? {
print("📱 ScannerViewModel.getPreviewLayer() - ⚠️ REQUESTING preview layer at \(Date())")
return cameraManager.previewLayer()
}
// MARK: - Scanning

View File

@@ -12,6 +12,7 @@ struct CookingModeView: View {
@Environment(\.dismiss) private var dismiss
@StateObject private var viewModel: CookingModeViewModel
@State private var showingAllSteps = false
@State private var previewLayer: AVCaptureVideoPreviewLayer?
init(recipe: Recipe) {
_viewModel = StateObject(wrappedValue: CookingModeViewModel(recipe: recipe))
@@ -21,8 +22,8 @@ struct CookingModeView: View {
NavigationStack {
ZStack {
// Camera preview background
if viewModel.isMonitoring {
CameraPreviewView(previewLayer: viewModel.getPreviewLayer())
if viewModel.isMonitoring, let previewLayer = previewLayer {
CameraPreviewView(previewLayer: previewLayer)
.ignoresSafeArea()
.opacity(0.3)
}
@@ -69,6 +70,7 @@ struct CookingModeView: View {
}
.task {
await viewModel.setupCamera()
previewLayer = viewModel.getPreviewLayer()
viewModel.startCamera()
}
.onDisappear {

View File

@@ -16,9 +16,15 @@ struct ScannerView: View {
@State private var detectedPlanes = 0
@State private var lastRaycastResult = ""
@State private var showARView = false
@State private var previewLayer: AVCaptureVideoPreviewLayer?
init() {
print("🔵 ScannerView.init() - View initialized at \(Date())")
}
var body: some View {
NavigationStack {
print("🔵 ScannerView.body - Body evaluated at \(Date()), showARView: \(showARView)")
return NavigationStack {
ZStack {
// AR camera preview or regular camera
if showARView {
@@ -28,8 +34,17 @@ struct ScannerView: View {
)
.ignoresSafeArea()
} else {
CameraPreviewView(previewLayer: viewModel.getPreviewLayer())
.ignoresSafeArea()
if let previewLayer = previewLayer {
CameraPreviewView(previewLayer: previewLayer)
.ignoresSafeArea()
} else {
Color.black
.ignoresSafeArea()
.overlay {
ProgressView("Initializing camera...")
.foregroundStyle(.white)
}
}
}
// Overlay UI
@@ -68,12 +83,19 @@ struct ScannerView: View {
}
}
.task {
print("🔵 ScannerView.task - Task started at \(Date())")
if !showARView {
print("🔵 ScannerView.task - Calling setupCamera()")
await viewModel.setupCamera()
print("🔵 ScannerView.task - Getting preview layer after setup")
previewLayer = viewModel.getPreviewLayer()
print("🔵 ScannerView.task - Preview layer set: \(previewLayer != nil)")
print("🔵 ScannerView.task - Calling startCamera()")
viewModel.startCamera()
}
}
.onDisappear {
print("🔵 ScannerView.onDisappear - Cleaning up at \(Date())")
viewModel.cleanup()
}
.alert("Camera Error", isPresented: .constant(viewModel.error != nil)) {
@@ -174,7 +196,11 @@ struct ScannerView: View {
withAnimation {
showARView.toggle()
if !showARView {
viewModel.startCamera()
Task {
await viewModel.setupCamera()
previewLayer = viewModel.getPreviewLayer()
viewModel.startCamera()
}
} else {
viewModel.stopCamera()
}
@@ -246,10 +272,12 @@ struct CameraPreviewView: UIViewRepresentable {
let previewLayer: AVCaptureVideoPreviewLayer
func makeUIView(context: Context) -> UIView {
print("🟢 CameraPreviewView.makeUIView() - Creating preview view at \(Date())")
let view = UIView(frame: .zero)
view.backgroundColor = .black
previewLayer.frame = view.bounds
view.layer.addSublayer(previewLayer)
print("🟢 CameraPreviewView.makeUIView() - Preview layer added to view")
return view
}