diff --git a/BusinessCard/Views/CardEditorView.swift b/BusinessCard/Views/CardEditorView.swift index c065d24..11717e8 100644 --- a/BusinessCard/Views/CardEditorView.swift +++ b/BusinessCard/Views/CardEditorView.swift @@ -46,10 +46,10 @@ struct CardEditorView: View { // Photo picker state @State private var pendingImageType: ImageType? // For showing PhotoSourcePicker - @State private var activeImageType: ImageType? // Tracks which type we're editing through the full flow + @State private var nextImageType: ImageType? // Stored when user selects action, used after dismiss + @State private var photoPickerImageType: ImageType? // For showing PhotoPickerWithCropper (item-based) + @State private var cameraImageType: ImageType? // For showing CameraWithCropper (item-based) @State private var pendingAction: PendingPhotoAction? // Action to take after source picker dismisses - @State private var showingPhotoPicker = false - @State private var showingCamera = false private enum PendingPhotoAction { case library @@ -225,17 +225,18 @@ struct CardEditorView: View { } .sheet(item: $pendingImageType, onDismiss: { // After source picker dismisses, show the appropriate picker - guard let action = pendingAction else { return } + guard let action = pendingAction, let imageType = nextImageType else { return } pendingAction = nil + nextImageType = nil // Small delay to ensure sheet is fully dismissed Task { @MainActor in try? await Task.sleep(for: .milliseconds(100)) switch action { case .library: - showingPhotoPicker = true + photoPickerImageType = imageType // Triggers fullScreenCover(item:) case .camera: - showingCamera = true + cameraImageType = imageType // Triggers fullScreenCover(item:) } } }) { imageType in @@ -243,11 +244,11 @@ struct CardEditorView: View { title: imageType.title, hasExistingPhoto: hasExistingPhoto(for: imageType), onSelectFromLibrary: { - activeImageType = imageType + nextImageType = imageType // Store for use after dismiss pendingAction = .library }, onTakePhoto: { - activeImageType = imageType + nextImageType = imageType // Store for use after dismiss pendingAction = .camera }, onRemovePhoto: { @@ -255,33 +256,29 @@ struct CardEditorView: View { } ) } - .fullScreenCover(isPresented: $showingPhotoPicker) { + .fullScreenCover(item: $photoPickerImageType) { imageType in NavigationStack { PhotoPickerWithCropper( - aspectRatio: activeImageType?.cropAspectRatio ?? .square, + aspectRatio: imageType.cropAspectRatio, onSave: { croppedData in - savePhoto(croppedData, for: activeImageType) - showingPhotoPicker = false - activeImageType = nil + savePhoto(croppedData, for: imageType) + photoPickerImageType = nil }, onCancel: { - showingPhotoPicker = false - activeImageType = nil + photoPickerImageType = nil } ) } } - .fullScreenCover(isPresented: $showingCamera) { + .fullScreenCover(item: $cameraImageType) { imageType in CameraWithCropper( - aspectRatio: activeImageType?.cropAspectRatio ?? .square, + aspectRatio: imageType.cropAspectRatio, onSave: { croppedData in - savePhoto(croppedData, for: activeImageType) - showingCamera = false - activeImageType = nil + savePhoto(croppedData, for: imageType) + cameraImageType = nil }, onCancel: { - showingCamera = false - activeImageType = nil + cameraImageType = nil } ) } @@ -734,8 +731,7 @@ private extension CardEditorView { } } - func savePhoto(_ data: Data, for imageType: ImageType?) { - guard let imageType else { return } + func savePhoto(_ data: Data, for imageType: ImageType) { switch imageType { case .profile: photoData = data case .cover: coverPhotoData = data @@ -805,6 +801,7 @@ private extension CardEditorView { bio: bio, accreditations: accreditations, photoData: photoData, + coverPhotoData: coverPhotoData, logoData: logoData ) @@ -837,6 +834,7 @@ private extension CardEditorView { bio: bio, accreditations: accreditations, photoData: photoData, + coverPhotoData: coverPhotoData, logoData: logoData ) diff --git a/BusinessCard/Views/Components/CameraWithCropper.swift b/BusinessCard/Views/Components/CameraWithCropper.swift index a74a543..a03d50f 100644 --- a/BusinessCard/Views/Components/CameraWithCropper.swift +++ b/BusinessCard/Views/Components/CameraWithCropper.swift @@ -54,6 +54,7 @@ struct CameraWithCropper: View { } } .transition(.move(edge: .trailing)) + .id(aspectRatio.ratio) // Force recreate cropper when aspect ratio changes } } .animation(.easeInOut(duration: Design.Animation.quick), value: showingCropper) diff --git a/BusinessCard/Views/Components/PhotoPickerWithCropper.swift b/BusinessCard/Views/Components/PhotoPickerWithCropper.swift index 3396e3d..4becfba 100644 --- a/BusinessCard/Views/Components/PhotoPickerWithCropper.swift +++ b/BusinessCard/Views/Components/PhotoPickerWithCropper.swift @@ -25,6 +25,7 @@ struct PhotoPickerWithCropper: View { @State private var selectedItem: PhotosPickerItem? @State private var imageData: Data? @State private var showingCropper = false + @State private var pickerRefreshID = UUID() // Force picker refresh after cancel var body: some View { PhotosPicker( @@ -38,6 +39,7 @@ struct PhotoPickerWithCropper: View { .photosPickerStyle(.inline) .photosPickerDisabledCapabilities([.selectionActions]) .ignoresSafeArea() + .id(pickerRefreshID) // Force recreate picker when ID changes .onChange(of: selectedItem) { _, newValue in guard let newValue else { return } Task { @MainActor in @@ -62,9 +64,12 @@ struct PhotoPickerWithCropper: View { showingCropper = false self.imageData = nil self.selectedItem = nil + // Generate new ID to force picker refresh, allowing same image to be reselected + pickerRefreshID = UUID() } } .transition(.move(edge: .trailing)) + .id(aspectRatio.ratio) // Force recreate cropper when aspect ratio changes } } .animation(.easeInOut(duration: Design.Animation.quick), value: showingCropper)