2025-08-21 19:37:03 +08:00

176 lines
6.2 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import SwiftUI
import PhotosUI
// MARK: - Photo Picker
///
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
self.onImageUploaded = onImageUploaded
self.onUploadProgress = onUploadProgress
}
// MARK: - UIViewControllerRepresentable
// MARK: - UIViewControllerRepresentable
/// PHPickerViewController
/// - Parameter context:
/// - Returns: PHPickerViewController
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
}
///
/// - Parameters:
/// - uiViewController:
/// - context:
func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {}
///
/// - Returns:
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
// MARK: - Coordinator
// MARK: -
/// PHPickerViewController
class Coordinator: NSObject, PHPickerViewControllerDelegate {
///
let parent: PhotoPicker
private let uploadService = ImageUploadService.shared
///
/// - Parameter parent:
init(_ parent: PhotoPicker) {
self.parent = parent
}
///
/// - Parameters:
/// - picker:
/// - results:
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
parent.selectedImages.removeAll()
let group = DispatchGroup()
var loadedImages: [Int: UIImage] = [:]
var uploadResults: [Int: ImageUploadService.UploadResults] = [:]
var lastError: Error?
//
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 {
// MARK: -
///
@Binding var selectedImage: UIImage?
///
let size: CGFloat
var onUploadComplete: ((Result<ImageUploadService.UploadResults, Error>) -> Void)?
// MARK: -
///
@State private var isImagePickerPresented = false
// MARK: -
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(
SVGImage(svgName: "Avatar")
.frame(width: size * 0.8, height: size * 0.8)
)
.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
)
}
}
}