From d27f665009822781a18594630a5d380b28196280 Mon Sep 17 00:00:00 2001 From: jinyaqiu Date: Thu, 21 Aug 2025 11:33:25 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=8D=95=E9=80=89=E5=A4=9A=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- wake/View/Components/Upload/MediaPicker.swift | 67 ++++++++++++++----- wake/View/Examples/MediaDemo.swift | 3 +- 2 files changed, 52 insertions(+), 18 deletions(-) diff --git a/wake/View/Components/Upload/MediaPicker.swift b/wake/View/Components/Upload/MediaPicker.swift index 9153f20..e12aa8e 100644 --- a/wake/View/Components/Upload/MediaPicker.swift +++ b/wake/View/Components/Upload/MediaPicker.swift @@ -56,23 +56,39 @@ struct MediaPicker: UIViewControllerRepresentable { let videoSelectionLimit: Int let onDismiss: (() -> Void)? let allowedMediaTypes: MediaTypeFilter + let selectionMode: SelectionMode + + /// 选择模式 + enum SelectionMode { + case single // 单选模式 + case multiple // 多选模式 + + var selectionLimit: Int { + switch self { + case .single: return 1 + case .multiple: return 0 // 0 表示不限制选择数量,由 imageSelectionLimit 和 videoSelectionLimit 控制 + } + } + } init(selectedMedia: Binding<[MediaType]>, imageSelectionLimit: Int = 10, videoSelectionLimit: Int = 10, allowedMediaTypes: MediaTypeFilter = .all, + selectionMode: SelectionMode = .multiple, onDismiss: (() -> Void)? = nil) { self._selectedMedia = selectedMedia self.imageSelectionLimit = imageSelectionLimit self.videoSelectionLimit = videoSelectionLimit self.allowedMediaTypes = allowedMediaTypes + self.selectionMode = selectionMode self.onDismiss = onDismiss } func makeUIViewController(context: Context) -> PHPickerViewController { var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared()) configuration.filter = allowedMediaTypes.pickerFilter - configuration.selectionLimit = 0 // 设置为0表示不限制选择数量,我们在代码中处理 + configuration.selectionLimit = selectionMode.selectionLimit configuration.preferredAssetRepresentationMode = .current let picker = PHPickerViewController(configuration: configuration) @@ -105,11 +121,14 @@ struct MediaPicker: UIViewControllerRepresentable { return } + // 如果是单选模式,清空之前的选择 + var processedMedia = parent.selectionMode == .single ? [] : parent.selectedMedia + // 统计当前已选择的图片和视频数量 var currentImageCount = 0 var currentVideoCount = 0 - for media in parent.selectedMedia { + for media in processedMedia { switch media { case .image: currentImageCount += 1 case .video: currentVideoCount += 1 @@ -123,11 +142,9 @@ struct MediaPicker: UIViewControllerRepresentable { for result in results { let itemProvider = result.itemProvider if itemProvider.hasItemConformingToTypeIdentifier(UTType.image.identifier) { - // 检查是否允许选择图片 guard parent.allowedMediaTypes != .videosOnly else { continue } newImages += 1 } else if itemProvider.hasItemConformingToTypeIdentifier(UTType.movie.identifier) { - // 检查是否允许选择视频 guard parent.allowedMediaTypes != .imagesOnly else { continue } newVideos += 1 } @@ -166,49 +183,49 @@ struct MediaPicker: UIViewControllerRepresentable { } // 处理选择的媒体 - processSelectedMedia(results: results, picker: picker) + processSelectedMedia(results: results, picker: picker, processedMedia: &processedMedia) } - private func processSelectedMedia(results: [PHPickerResult], picker: PHPickerViewController) { - var processedMedia = parent.selectedMedia + private func processSelectedMedia(results: [PHPickerResult], + picker: PHPickerViewController, + processedMedia: inout [MediaType]) { let group = DispatchGroup() + let mediaCollector = MediaCollector() for result in results { let itemProvider = result.itemProvider if itemProvider.hasItemConformingToTypeIdentifier(UTType.image.identifier) { - // 检查是否允许选择图片 guard parent.allowedMediaTypes != .videosOnly else { continue } group.enter() processImage(itemProvider: itemProvider) { media in if let media = media { - DispatchQueue.main.async { - processedMedia.append(media) - } + mediaCollector.add(media: media) } group.leave() } } else if itemProvider.hasItemConformingToTypeIdentifier(UTType.movie.identifier) { - // 检查是否允许选择视频 guard parent.allowedMediaTypes != .imagesOnly else { continue } group.enter() processVideo(itemProvider: itemProvider) { media in if let media = media { - DispatchQueue.main.async { - processedMedia.append(media) - } + mediaCollector.add(media: media) } group.leave() } } } + // Create a local copy of the parent reference + let parent = self.parent + group.notify(queue: .main) { - self.parent.selectedMedia = processedMedia + let finalMedia = mediaCollector.mediaItems + parent.selectedMedia = finalMedia picker.dismiss(animated: true) { - self.parent.onDismiss?() + parent.onDismiss?() } } } @@ -268,3 +285,19 @@ struct MediaPicker: UIViewControllerRepresentable { } } } + +// Helper class to collect media items in a thread-safe way +private class MediaCollector { + private let queue = DispatchQueue(label: "com.example.MediaCollector", attributes: .concurrent) + private var _mediaItems: [MediaType] = [] + + var mediaItems: [MediaType] { + queue.sync { _mediaItems } + } + + func add(media: MediaType) { + queue.async(flags: .barrier) { [weak self] in + self?._mediaItems.append(media) + } + } +} diff --git a/wake/View/Examples/MediaDemo.swift b/wake/View/Examples/MediaDemo.swift index a7578d6..b41241b 100644 --- a/wake/View/Examples/MediaDemo.swift +++ b/wake/View/Examples/MediaDemo.swift @@ -27,7 +27,8 @@ struct MediaUploadDemo: View { selectedMedia: $uploadManager.selectedMedia, imageSelectionLimit: 1, videoSelectionLimit: 0, - allowedMediaTypes: .imagesOnly, + allowedMediaTypes: .imagesOnly, // This needs to come before selectionMode + selectionMode: .single, // This was moved after allowedMediaTypes onDismiss: { showMediaPicker = false // 当媒体选择器关闭时,如果有选中的媒体,开始上传