diff --git a/wake/View/Components/Upload/MediaPicker.swift b/wake/View/Components/Upload/MediaPicker.swift index dfb3f57..e10dc01 100644 --- a/wake/View/Components/Upload/MediaPicker.swift +++ b/wake/View/Components/Upload/MediaPicker.swift @@ -38,29 +38,14 @@ public enum MediaType: Equatable { struct MediaPicker: UIViewControllerRepresentable { @Binding var selectedMedia: [MediaType] - let selectionLimit: Int + let imageSelectionLimit: Int + let videoSelectionLimit: Int let onDismiss: (() -> Void)? - func makeUIViewController(context: Context) -> PHPickerViewController { - var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared()) - configuration.filter = .any(of: [.videos, .images]) - configuration.selectionLimit = selectionLimit - configuration.preferredAssetRepresentationMode = .current - - let picker = PHPickerViewController(configuration: configuration) - picker.delegate = context.coordinator - return picker - } - - func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {} - - func makeCoordinator() -> Coordinator { - Coordinator(self) - } - class Coordinator: NSObject, PHPickerViewControllerDelegate { let parent: MediaPicker private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "com.example.app", category: "MediaPicker") + internal var currentPicker: PHPickerViewController? // 将 private 修改为 internal init(_ parent: MediaPicker) { self.parent = parent @@ -72,7 +57,68 @@ struct MediaPicker: UIViewControllerRepresentable { return } - var processedMedia: [MediaType] = [] + // 统计当前已选择的图片和视频数量 + var currentImageCount = 0 + var currentVideoCount = 0 + + for media in parent.selectedMedia { + switch media { + case .image: currentImageCount += 1 + case .video: currentVideoCount += 1 + } + } + + // 检查新选择的项目 + var newImages = 0 + var newVideos = 0 + + for result in results { + let itemProvider = result.itemProvider + if itemProvider.hasItemConformingToTypeIdentifier(UTType.image.identifier) { + newImages += 1 + } else if itemProvider.hasItemConformingToTypeIdentifier(UTType.movie.identifier) { + newVideos += 1 + } + } + + // 检查是否超出限制 + if (currentImageCount + newImages > parent.imageSelectionLimit) || + (currentVideoCount + newVideos > parent.videoSelectionLimit) { + + // 准备错误信息 + var message = "选择超出限制:\n" + var limits: [String] = [] + + if currentImageCount + newImages > parent.imageSelectionLimit { + limits.append("图片最多选择\(parent.imageSelectionLimit)张") + } + if currentVideoCount + newVideos > parent.videoSelectionLimit { + limits.append("视频最多选择\(parent.videoSelectionLimit)个") + } + + message += limits.joined(separator: "\n") + + // 显示提示 + let alert = UIAlertController( + title: "提示", + message: message, + preferredStyle: .alert + ) + alert.addAction(UIAlertAction(title: "好的", style: .default) { _ in + // 不清空选择,允许用户继续选择 + }) + + // 显示提示框 + picker.present(alert, animated: true) + return + } + + // 如果没有超出限制,处理选择的媒体 + processSelectedMedia(results: results, picker: picker) + } + + private func processSelectedMedia(results: [PHPickerResult], picker: PHPickerViewController) { + var processedMedia = parent.selectedMedia let group = DispatchGroup() for result in results { @@ -82,7 +128,9 @@ struct MediaPicker: UIViewControllerRepresentable { group.enter() processImage(itemProvider: itemProvider) { media in if let media = media { - processedMedia.append(media) + DispatchQueue.main.async { + processedMedia.append(media) + } } group.leave() } @@ -90,7 +138,9 @@ struct MediaPicker: UIViewControllerRepresentable { group.enter() processVideo(itemProvider: itemProvider) { media in if let media = media { - processedMedia.append(media) + DispatchQueue.main.async { + processedMedia.append(media) + } } group.leave() } @@ -99,8 +149,9 @@ struct MediaPicker: UIViewControllerRepresentable { group.notify(queue: .main) { self.parent.selectedMedia = processedMedia - self.printMediaInfo(media: processedMedia) - self.parent.onDismiss?() + picker.dismiss(animated: true) { + self.parent.onDismiss?() + } } } @@ -147,55 +198,26 @@ struct MediaPicker: UIViewControllerRepresentable { } } } + } + + func makeUIViewController(context: Context) -> PHPickerViewController { + var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared()) + configuration.filter = .any(of: [.videos, .images]) + configuration.selectionLimit = 0 // 设置为0表示不限制选择数量,我们在代码中处理 + configuration.preferredAssetRepresentationMode = .current - private func printMediaInfo(media: [MediaType]) { - print("=== Selected Media Information ===") - for (index, media) in media.enumerated() { - print("\nItem \(index + 1):") - - switch media { - case .image(let image): - print("Type: Image") - print("Dimensions: \(Int(image.size.width))x\(Int(image.size.height))") - if let data = image.jpegData(compressionQuality: 1.0) { - print("File Size: \(formatFileSize(Int64(data.count)))") - } - - case .video(let url, _): - print("Type: Video") - print("File Name: \(url.lastPathComponent)") - print("File Path: \(url.path)") - - if let attributes = try? FileManager.default.attributesOfItem(atPath: url.path), - let fileSize = attributes[.size] as? Int64 { - print("File Size: \(formatFileSize(fileSize))") - } - - let asset = AVURLAsset(url: url) - let duration = asset.duration.seconds - print("Duration: \(formatTimeInterval(duration))") - - if let track = asset.tracks(withMediaType: .video).first { - let size = track.naturalSize - print("Video Dimensions: \(Int(size.width))x\(Int(size.height))") - } - } - } - print("================================\n") - } + let picker = PHPickerViewController(configuration: configuration) + picker.delegate = context.coordinator + context.coordinator.currentPicker = picker - private func formatFileSize(_ bytes: Int64) -> String { - let formatter = ByteCountFormatter() - formatter.allowedUnits = [.useBytes, .useKB, .useMB, .useGB] - formatter.countStyle = .file - return formatter.string(fromByteCount: bytes) - } - - private func formatTimeInterval(_ interval: TimeInterval) -> String { - let formatter = DateComponentsFormatter() - formatter.allowedUnits = [.hour, .minute, .second] - formatter.zeroFormattingBehavior = .pad - return formatter.string(from: interval) ?? "00:00" - } + return picker + } + + func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) { + // 更新视图控制器(如果需要) + } + + func makeCoordinator() -> Coordinator { + Coordinator(self) } } diff --git a/wake/View/Components/Upload/MediaUpload.swift b/wake/View/Components/Upload/MediaUpload.swift index 36515c0..e6bde66 100644 --- a/wake/View/Components/Upload/MediaUpload.swift +++ b/wake/View/Components/Upload/MediaUpload.swift @@ -151,6 +151,15 @@ struct MediaUploadExample: View { @StateObject private var uploadManager = MediaUploadManager() @State private var showMediaPicker = false + // 添加图片和视频选择限制参数 + let imageSelectionLimit: Int + let videoSelectionLimit: Int + + init(imageSelectionLimit: Int = 10, videoSelectionLimit: Int = 10) { + self.imageSelectionLimit = imageSelectionLimit + self.videoSelectionLimit = videoSelectionLimit + } + var body: some View { NavigationView { VStack(spacing: 20) { @@ -166,6 +175,21 @@ struct MediaUploadExample: View { } .padding(.horizontal) + // 显示选择限制信息 + VStack(alignment: .leading, spacing: 4) { + Text("选择限制:") + .font(.subheadline) + .foregroundColor(.secondary) + Text("• 最多选择 \(imageSelectionLimit) 张图片") + .font(.caption) + .foregroundColor(.secondary) + Text("• 最多选择 \(videoSelectionLimit) 个视频") + .font(.caption) + .foregroundColor(.secondary) + } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal) + // 显示已选媒体 MediaSelectionView(uploadManager: uploadManager) @@ -188,7 +212,8 @@ struct MediaUploadExample: View { .sheet(isPresented: $showMediaPicker) { MediaPicker( selectedMedia: $uploadManager.selectedMedia, - selectionLimit: 5, + imageSelectionLimit: imageSelectionLimit, + videoSelectionLimit: videoSelectionLimit, onDismiss: { showMediaPicker = false } ) } @@ -293,6 +318,10 @@ private struct MediaThumbnailView: View { } #Preview { - MediaUploadExample() - .environmentObject(AuthState.shared) + // 在预览中显示自定义限制 + MediaUploadExample( + imageSelectionLimit: 5, + videoSelectionLimit: 2 + ) + .environmentObject(AuthState.shared) } \ No newline at end of file diff --git a/wake/View/Examples/MediaDemo.swift b/wake/View/Examples/MediaDemo.swift index 1f1e97d..a53bbed 100644 --- a/wake/View/Examples/MediaDemo.swift +++ b/wake/View/Examples/MediaDemo.swift @@ -25,7 +25,8 @@ struct MediaUploadDemo: View { .sheet(isPresented: $showMediaPicker) { MediaPicker( selectedMedia: $uploadManager.selectedMedia, - selectionLimit: 10, + imageSelectionLimit: 2, + videoSelectionLimit: 2, onDismiss: { showMediaPicker = false // 当媒体选择器关闭时,如果有选中的媒体,开始上传