2025-08-19 20:36:00 +08:00

214 lines
8.8 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
///
/// 使UIViewControllerRepresentablePHPickerViewControllerSwiftUI
struct PhotoPicker: UIViewControllerRepresentable {
// MARK: - Properties
///
@Binding var selectedImages: [UIImage]
/// 1
let selectionLimit: Int
///
let filter: PHPickerFilter
///
var onImageUploaded: ((Result<ImageUploaderGetID.UploadResult, Error>) -> Void)?
// MARK: - Initialization
///
/// - Parameters:
/// - selectedImages:
/// - selectionLimit: 1
/// - filter:
/// - onImageUploaded:
init(
selectedImages: Binding<[UIImage]>,
selectionLimit: Int = 1,
filter: PHPickerFilter = .images,
onImageUploaded: ((Result<ImageUploaderGetID.UploadResult, Error>) -> Void)? = nil
) {
self._selectedImages = selectedImages
self.selectionLimit = selectionLimit
self.filter = filter
self.onImageUploaded = onImageUploaded
}
// MARK: - UIViewControllerRepresentable
/// 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
}
///
func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {}
/// PHPickerViewController
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
// MARK: - Coordinator
/// PHPickerViewController
class Coordinator: NSObject, PHPickerViewControllerDelegate {
///
let parent: PhotoPicker
///
private let uploader = ImageUploaderGetID()
init(_ parent: PhotoPicker) {
self.parent = parent
}
///
/// - Parameters:
/// - picker:
/// - results:
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
//
parent.selectedImages.removeAll()
// 使DispatchGroup
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) { [weak self] (image, error) in
if let image = image as? UIImage {
//
loadedImages[index] = image
//
self?.uploader.uploadImage(image) { result in
// 线
DispatchQueue.main.async {
switch result {
case .success(let uploadResult):
print("✅ 上传成功fileId: \(uploadResult.fileId)")
print("📂 文件信息:")
print(" - 文件名: \(uploadResult.fileName)")
print(" - 文件大小: \(uploadResult.fileSize) 字节")
print(" - 文件URL: \(uploadResult.fileUrl)")
//
self?.parent.onImageUploaded?(.success(uploadResult))
case .failure(let error):
print("❌ 上传失败: \(error.localizedDescription)")
//
self?.parent.onImageUploaded?(.failure(error))
}
}
}
}
group.leave() //
}
} else {
group.leave() //
}
}
//
group.notify(queue: .main) {
//
let sortedImages = loadedImages.sorted { $0.key < $1.key }.map { $0.value }
self.parent.selectedImages.append(contentsOf: sortedImages)
//
picker.dismiss(animated: true)
}
}
}
}
// MARK: - AvatarUploader
///
///
struct AvatarUploader: View {
// MARK: - Properties
///
@Binding var selectedImage: UIImage?
///
let size: CGFloat
///
var onUploadComplete: ((Result<ImageUploaderGetID.UploadResult, Error>) -> Void)?
// MARK: - State
///
@State private var isImagePickerPresented = false
// MARK: - Body
var body: some View {
//
Button(action: { isImagePickerPresented = true }) {
ZStack {
if let selectedImage = selectedImage {
//
Image(uiImage: selectedImage)
.resizable()
.scaledToFill()
.frame(width: size, height: size)
.clipShape(RoundedRectangle(cornerRadius: size * 0.1))
} else {
//
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()) //
}
.buttonStyle(PlainButtonStyle()) // 使
.sheet(isPresented: $isImagePickerPresented) {
//
PhotoPicker(
selectedImages: Binding(
get: { [selectedImage].compactMap { $0 } },
set: { images in
selectedImage = images.first
}
),
selectionLimit: 1, //
onImageUploaded: { result in
//
onUploadComplete?(result)
}
)
}
}
}