feat(image): 이미지 선택 후 크롭 편집 흐름을 적용한다

This commit is contained in:
Yu Sung
2026-03-17 14:39:42 +09:00
parent 408c3b7619
commit 99fcf3a94c
8 changed files with 784 additions and 93 deletions

View File

@@ -13,6 +13,11 @@ struct ProfileUpdateView: View {
@StateObject var keyboardHandler = KeyboardHandler()
@State private var isShowPhotoPicker = false
@State private var selectedPickedImage: UIImage?
@State private var cropSourceImage: UIImage?
@State private var isShowImageCropper = false
@State private var selectedProfileImage: UIImage?
@State private var isImageLoading = false
@State private var isShowSelectTagView = false
let refresh: () -> Void
@@ -297,7 +302,13 @@ struct ProfileUpdateView: View {
VStack(spacing: 0) {
if let profileResponse = viewModel.profileResponse {
ZStack {
if profileResponse.profileUrl.trimmingCharacters(in: .whitespaces).count > 0 {
if let selectedProfileImage {
Image(uiImage: selectedProfileImage)
.resizable()
.scaledToFill()
.frame(width: 80, height: 80, alignment: .top)
.clipShape(Circle())
} else if profileResponse.profileUrl.trimmingCharacters(in: .whitespaces).count > 0 {
KFImage(URL(string: profileResponse.profileUrl))
.cancelOnDisappear(true)
.downsampling(
@@ -407,14 +418,6 @@ struct ProfileUpdateView: View {
}
}
if isShowPhotoPicker {
ImagePicker(
isShowing: $isShowPhotoPicker,
selectedImage: $viewModel.profileImage,
sourceType: .photoLibrary
)
}
if isShowSelectTagView {
GeometryReader { proxy in
VStack {
@@ -437,6 +440,16 @@ struct ProfileUpdateView: View {
}
.edgesIgnoringSafeArea(.bottom)
}
if isImageLoading {
ZStack {
Color.black.opacity(0.45)
.edgesIgnoringSafeArea(.all)
ProgressView()
.progressViewStyle(CircularProgressViewStyle(tint: .white))
}
}
}
.offset(y: keyboardHandler.keyboardHeight > 0 ? -keyboardHandler.keyboardHeight + 15.3 : 0)
.edgesIgnoringSafeArea(.bottom)
@@ -449,6 +462,52 @@ struct ProfileUpdateView: View {
viewModel.getMyProfile()
}
.sodaToast(isPresented: $viewModel.isShowPopup, message: viewModel.errorMessage, autohideIn: 2)
.onChange(of: selectedPickedImage, perform: { newImage in
guard let newImage else {
return
}
isImageLoading = true
DispatchQueue.global(qos: .userInitiated).async {
let normalizedImage = newImage.normalizedForCrop()
DispatchQueue.main.async {
isImageLoading = false
selectedPickedImage = nil
cropSourceImage = normalizedImage
isShowImageCropper = true
}
}
})
.onDisappear {
isImageLoading = false
}
.sheet(isPresented: $isShowImageCropper, onDismiss: {
cropSourceImage = nil
}) {
if let cropSourceImage {
ImageCropEditorView(
image: cropSourceImage,
aspectPolicy: .square,
onCancel: {
isShowImageCropper = false
},
onComplete: { croppedImage in
selectedProfileImage = croppedImage
viewModel.profileImage = croppedImage
isShowImageCropper = false
}
)
}
}
if isShowPhotoPicker {
ImagePicker(
isShowing: $isShowPhotoPicker,
selectedImage: $selectedPickedImage,
sourceType: .photoLibrary
)
}
}
}
}