feat: 限制选择类型
This commit is contained in:
parent
7e807f6a26
commit
06f7c1e367
@ -36,16 +36,64 @@ public enum MediaType: Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
enum MediaTypeFilter {
|
||||
case imagesOnly
|
||||
case videosOnly
|
||||
case all
|
||||
|
||||
var pickerFilter: PHPickerFilter {
|
||||
switch self {
|
||||
case .imagesOnly: return .images
|
||||
case .videosOnly: return .videos
|
||||
case .all: return .any(of: [.videos, .images])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MediaPicker: UIViewControllerRepresentable {
|
||||
@Binding var selectedMedia: [MediaType]
|
||||
let imageSelectionLimit: Int
|
||||
let videoSelectionLimit: Int
|
||||
let onDismiss: (() -> Void)?
|
||||
let allowedMediaTypes: MediaTypeFilter
|
||||
|
||||
init(selectedMedia: Binding<[MediaType]>,
|
||||
imageSelectionLimit: Int = 10,
|
||||
videoSelectionLimit: Int = 10,
|
||||
allowedMediaTypes: MediaTypeFilter = .all,
|
||||
onDismiss: (() -> Void)? = nil) {
|
||||
self._selectedMedia = selectedMedia
|
||||
self.imageSelectionLimit = imageSelectionLimit
|
||||
self.videoSelectionLimit = videoSelectionLimit
|
||||
self.allowedMediaTypes = allowedMediaTypes
|
||||
self.onDismiss = onDismiss
|
||||
}
|
||||
|
||||
func makeUIViewController(context: Context) -> PHPickerViewController {
|
||||
var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
|
||||
configuration.filter = allowedMediaTypes.pickerFilter
|
||||
configuration.selectionLimit = 0 // 设置为0表示不限制选择数量,我们在代码中处理
|
||||
configuration.preferredAssetRepresentationMode = .current
|
||||
|
||||
let picker = PHPickerViewController(configuration: configuration)
|
||||
picker.delegate = context.coordinator
|
||||
context.coordinator.currentPicker = picker
|
||||
|
||||
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
|
||||
internal var currentPicker: PHPickerViewController?
|
||||
|
||||
init(_ parent: MediaPicker) {
|
||||
self.parent = parent
|
||||
@ -75,8 +123,12 @@ 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
|
||||
}
|
||||
}
|
||||
@ -89,10 +141,10 @@ struct MediaPicker: UIViewControllerRepresentable {
|
||||
var message = "选择超出限制:\n"
|
||||
var limits: [String] = []
|
||||
|
||||
if currentImageCount + newImages > parent.imageSelectionLimit {
|
||||
if currentImageCount + newImages > parent.imageSelectionLimit && parent.allowedMediaTypes != .videosOnly {
|
||||
limits.append("图片最多选择\(parent.imageSelectionLimit)张")
|
||||
}
|
||||
if currentVideoCount + newVideos > parent.videoSelectionLimit {
|
||||
if currentVideoCount + newVideos > parent.videoSelectionLimit && parent.allowedMediaTypes != .imagesOnly {
|
||||
limits.append("视频最多选择\(parent.videoSelectionLimit)个")
|
||||
}
|
||||
|
||||
@ -113,7 +165,7 @@ struct MediaPicker: UIViewControllerRepresentable {
|
||||
return
|
||||
}
|
||||
|
||||
// 如果没有超出限制,处理选择的媒体
|
||||
// 处理选择的媒体
|
||||
processSelectedMedia(results: results, picker: picker)
|
||||
}
|
||||
|
||||
@ -125,6 +177,9 @@ struct MediaPicker: UIViewControllerRepresentable {
|
||||
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 {
|
||||
@ -135,6 +190,9 @@ struct MediaPicker: UIViewControllerRepresentable {
|
||||
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 {
|
||||
@ -183,41 +241,30 @@ struct MediaPicker: UIViewControllerRepresentable {
|
||||
}
|
||||
try FileManager.default.copyItem(at: videoURL, to: targetURL)
|
||||
|
||||
MediaUtils.extractFirstFrame(from: targetURL) { result in
|
||||
switch result {
|
||||
case .success(let thumbnail):
|
||||
if let thumbnail = self.generateThumbnail(for: targetURL) {
|
||||
completion(.video(targetURL, thumbnail))
|
||||
case .failure(let error):
|
||||
self.logger.error("Failed to extract video thumbnail: \(error.localizedDescription)")
|
||||
} else {
|
||||
completion(.video(targetURL, nil))
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
self.logger.error("Failed to copy video file: \(error.localizedDescription)")
|
||||
completion(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 generateThumbnail(for videoURL: URL) -> UIImage? {
|
||||
let asset = AVAsset(url: videoURL)
|
||||
let imageGenerator = AVAssetImageGenerator(asset: asset)
|
||||
imageGenerator.appliesPreferredTrackTransform = true
|
||||
|
||||
let picker = PHPickerViewController(configuration: configuration)
|
||||
picker.delegate = context.coordinator
|
||||
context.coordinator.currentPicker = picker
|
||||
|
||||
return picker
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {
|
||||
// 更新视图控制器(如果需要)
|
||||
}
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator(self)
|
||||
do {
|
||||
let cgImage = try imageGenerator.copyCGImage(at: CMTime(seconds: 0, preferredTimescale: 1), actualTime: nil)
|
||||
return UIImage(cgImage: cgImage)
|
||||
} catch {
|
||||
logger.error("Failed to generate thumbnail: \(error.localizedDescription)")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,8 +25,9 @@ struct MediaUploadDemo: View {
|
||||
.sheet(isPresented: $showMediaPicker) {
|
||||
MediaPicker(
|
||||
selectedMedia: $uploadManager.selectedMedia,
|
||||
imageSelectionLimit: 2,
|
||||
videoSelectionLimit: 2,
|
||||
imageSelectionLimit: 1,
|
||||
videoSelectionLimit: 0,
|
||||
allowedMediaTypes: .imagesOnly,
|
||||
onDismiss: {
|
||||
showMediaPicker = false
|
||||
// 当媒体选择器关闭时,如果有选中的媒体,开始上传
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user