123 lines
4.5 KiB
Swift

import SwiftUI
import PhotosUI
struct PhotoPicker: UIViewControllerRepresentable {
@Binding var selectedImages: [UIImage]
let selectionLimit: Int
let filter: PHPickerFilter
init(selectedImages: Binding<[UIImage]>, selectionLimit: Int = 1, filter: PHPickerFilter = .images) {
self._selectedImages = selectedImages
self.selectionLimit = selectionLimit
self.filter = filter
}
func makeUIViewController(context: Context) -> PHPickerViewController {
var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
configuration.filter = filter
configuration.selectionLimit = selectionLimit
configuration.preferredAssetRepresentationMode = .current
let picker = PHPickerViewController(configuration: configuration)
picker.delegate = context.coordinator
return picker
}
func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, PHPickerViewControllerDelegate {
let parent: PhotoPicker
init(_ parent: PhotoPicker) {
self.parent = parent
}
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
parent.selectedImages.removeAll()
let group = DispatchGroup()
var loadedImages: [Int: UIImage] = [:]
for (index, result) in results.enumerated() {
group.enter()
if result.itemProvider.canLoadObject(ofClass: UIImage.self) {
result.itemProvider.loadObject(ofClass: UIImage.self) { (image, error) in
if let image = image as? UIImage {
loadedImages[index] = image
}
group.leave()
}
} else {
group.leave()
}
}
group.notify(queue: .main) {
// Sort the images by their original index to maintain selection order
let sortedImages = loadedImages.sorted { $0.key < $1.key }.map { $0.value }
self.parent.selectedImages.append(contentsOf: sortedImages)
// Dismiss the picker
picker.dismiss(animated: true)
}
}
}
}
// MARK: - Avatar Uploader Component
struct AvatarUploader: View {
@Binding var selectedImage: UIImage?
let size: CGFloat
@State private var isImagePickerPresented = false
var body: some View {
Button(action: {
isImagePickerPresented = true
}) {
ZStack {
// Avatar Image or Placeholder
if let selectedImage = selectedImage {
Image(uiImage: selectedImage)
.resizable()
.scaledToFill()
.frame(width: size, height: size)
.clipShape(RoundedRectangle(cornerRadius: size * 0.1))
} else {
// Default avatar container
Color.gray.opacity(0.1)
.frame(width: size, height: size)
.overlay(
Image(systemName: "person.crop.circle")
.font(.system(size: size * 0.5))
.foregroundColor(.gray)
)
.clipShape(RoundedRectangle(cornerRadius: size * 0.1))
.overlay(
RoundedRectangle(cornerRadius: size * 0.1)
.stroke(Color.gray.opacity(0.3), lineWidth: 1)
)
}
}
.frame(width: size, height: size)
.contentShape(Rectangle()) // Make the entire area tappable
}
.buttonStyle(PlainButtonStyle()) // Remove button highlight effect
.sheet(isPresented: $isImagePickerPresented) {
PhotoPicker(
selectedImages: Binding(
get: { [selectedImage].compactMap { $0 } },
set: { images in
selectedImage = images.first
}
),
selectionLimit: 1
)
}
}
}