import SwiftUI import PhotosUI /// 照片选择器,封装了系统相册选择功能 struct PhotoPicker: UIViewControllerRepresentable { // MARK: - 属性 /// 绑定的已选图片数组 @Binding var selectedImages: [UIImage] /// 最多可选图片数量 let selectionLimit: Int /// 图片过滤器,默认为图片类型 let filter: PHPickerFilter // MARK: - 初始化方法 /// 初始化照片选择器 /// - Parameters: /// - selectedImages: 绑定的已选图片数组 /// - selectionLimit: 最多可选图片数量,默认为1 /// - filter: 图片过滤器,默认为.images init(selectedImages: Binding<[UIImage]>, selectionLimit: Int = 1, filter: PHPickerFilter = .images) { self._selectedImages = selectedImages self.selectionLimit = selectionLimit self.filter = filter } // 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: - 协调器类 /// 协调器,处理PHPickerViewController的代理方法 class Coordinator: NSObject, PHPickerViewControllerDelegate { /// 父视图引用 let parent: PhotoPicker /// 初始化方法 /// - Parameter parent: 父视图 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) { (image, error) in if let image = image as? UIImage { // 保存加载的图片,保持原始顺序 loadedImages[index] = image } 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: - 头像上传组件 /// 头像上传视图,提供头像选择功能 struct AvatarUploader: View { // MARK: - 属性 /// 当前选中的头像图片 @Binding var selectedImage: UIImage? /// 头像尺寸 let size: CGFloat // MARK: - 状态 /// 是否显示图片选择器 @State private var isImagePickerPresented = false // MARK: - 视图 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 ) } } }