feat: 单选多选

This commit is contained in:
jinyaqiu 2025-08-21 11:33:25 +08:00
parent 06f7c1e367
commit d27f665009
2 changed files with 52 additions and 18 deletions

View File

@ -56,23 +56,39 @@ struct MediaPicker: UIViewControllerRepresentable {
let videoSelectionLimit: Int let videoSelectionLimit: Int
let onDismiss: (() -> Void)? let onDismiss: (() -> Void)?
let allowedMediaTypes: MediaTypeFilter 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]>, init(selectedMedia: Binding<[MediaType]>,
imageSelectionLimit: Int = 10, imageSelectionLimit: Int = 10,
videoSelectionLimit: Int = 10, videoSelectionLimit: Int = 10,
allowedMediaTypes: MediaTypeFilter = .all, allowedMediaTypes: MediaTypeFilter = .all,
selectionMode: SelectionMode = .multiple,
onDismiss: (() -> Void)? = nil) { onDismiss: (() -> Void)? = nil) {
self._selectedMedia = selectedMedia self._selectedMedia = selectedMedia
self.imageSelectionLimit = imageSelectionLimit self.imageSelectionLimit = imageSelectionLimit
self.videoSelectionLimit = videoSelectionLimit self.videoSelectionLimit = videoSelectionLimit
self.allowedMediaTypes = allowedMediaTypes self.allowedMediaTypes = allowedMediaTypes
self.selectionMode = selectionMode
self.onDismiss = onDismiss self.onDismiss = onDismiss
} }
func makeUIViewController(context: Context) -> PHPickerViewController { func makeUIViewController(context: Context) -> PHPickerViewController {
var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared()) var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
configuration.filter = allowedMediaTypes.pickerFilter configuration.filter = allowedMediaTypes.pickerFilter
configuration.selectionLimit = 0 // 0 configuration.selectionLimit = selectionMode.selectionLimit
configuration.preferredAssetRepresentationMode = .current configuration.preferredAssetRepresentationMode = .current
let picker = PHPickerViewController(configuration: configuration) let picker = PHPickerViewController(configuration: configuration)
@ -105,11 +121,14 @@ struct MediaPicker: UIViewControllerRepresentable {
return return
} }
//
var processedMedia = parent.selectionMode == .single ? [] : parent.selectedMedia
// //
var currentImageCount = 0 var currentImageCount = 0
var currentVideoCount = 0 var currentVideoCount = 0
for media in parent.selectedMedia { for media in processedMedia {
switch media { switch media {
case .image: currentImageCount += 1 case .image: currentImageCount += 1
case .video: currentVideoCount += 1 case .video: currentVideoCount += 1
@ -123,11 +142,9 @@ struct MediaPicker: UIViewControllerRepresentable {
for result in results { for result in results {
let itemProvider = result.itemProvider let itemProvider = result.itemProvider
if itemProvider.hasItemConformingToTypeIdentifier(UTType.image.identifier) { if itemProvider.hasItemConformingToTypeIdentifier(UTType.image.identifier) {
//
guard parent.allowedMediaTypes != .videosOnly else { continue } guard parent.allowedMediaTypes != .videosOnly else { continue }
newImages += 1 newImages += 1
} else if itemProvider.hasItemConformingToTypeIdentifier(UTType.movie.identifier) { } else if itemProvider.hasItemConformingToTypeIdentifier(UTType.movie.identifier) {
//
guard parent.allowedMediaTypes != .imagesOnly else { continue } guard parent.allowedMediaTypes != .imagesOnly else { continue }
newVideos += 1 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) { private func processSelectedMedia(results: [PHPickerResult],
var processedMedia = parent.selectedMedia picker: PHPickerViewController,
processedMedia: inout [MediaType]) {
let group = DispatchGroup() let group = DispatchGroup()
let mediaCollector = MediaCollector()
for result in results { for result in results {
let itemProvider = result.itemProvider let itemProvider = result.itemProvider
if itemProvider.hasItemConformingToTypeIdentifier(UTType.image.identifier) { if itemProvider.hasItemConformingToTypeIdentifier(UTType.image.identifier) {
//
guard parent.allowedMediaTypes != .videosOnly else { continue } guard parent.allowedMediaTypes != .videosOnly else { continue }
group.enter() group.enter()
processImage(itemProvider: itemProvider) { media in processImage(itemProvider: itemProvider) { media in
if let media = media { if let media = media {
DispatchQueue.main.async { mediaCollector.add(media: media)
processedMedia.append(media)
}
} }
group.leave() group.leave()
} }
} else if itemProvider.hasItemConformingToTypeIdentifier(UTType.movie.identifier) { } else if itemProvider.hasItemConformingToTypeIdentifier(UTType.movie.identifier) {
//
guard parent.allowedMediaTypes != .imagesOnly else { continue } guard parent.allowedMediaTypes != .imagesOnly else { continue }
group.enter() group.enter()
processVideo(itemProvider: itemProvider) { media in processVideo(itemProvider: itemProvider) { media in
if let media = media { if let media = media {
DispatchQueue.main.async { mediaCollector.add(media: media)
processedMedia.append(media)
}
} }
group.leave() group.leave()
} }
} }
} }
// Create a local copy of the parent reference
let parent = self.parent
group.notify(queue: .main) { group.notify(queue: .main) {
self.parent.selectedMedia = processedMedia let finalMedia = mediaCollector.mediaItems
parent.selectedMedia = finalMedia
picker.dismiss(animated: true) { 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)
}
}
}

View File

@ -27,7 +27,8 @@ struct MediaUploadDemo: View {
selectedMedia: $uploadManager.selectedMedia, selectedMedia: $uploadManager.selectedMedia,
imageSelectionLimit: 1, imageSelectionLimit: 1,
videoSelectionLimit: 0, videoSelectionLimit: 0,
allowedMediaTypes: .imagesOnly, allowedMediaTypes: .imagesOnly, // This needs to come before selectionMode
selectionMode: .single, // This was moved after allowedMediaTypes
onDismiss: { onDismiss: {
showMediaPicker = false showMediaPicker = false
// //