Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>

This commit is contained in:
Matt Bruce 2026-01-09 09:55:18 -06:00
parent c2056e719d
commit 9c4d9304e6
3 changed files with 28 additions and 24 deletions

View File

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

View File

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

View File

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