feat: 限制选择数量
This commit is contained in:
parent
4670b27942
commit
7e807f6a26
@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func printMediaInfo(media: [MediaType]) {
|
||||
print("=== Selected Media Information ===")
|
||||
for (index, media) in media.enumerated() {
|
||||
print("\nItem \(index + 1):")
|
||||
func makeUIViewController(context: Context) -> PHPickerViewController {
|
||||
var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
|
||||
configuration.filter = .any(of: [.videos, .images])
|
||||
configuration.selectionLimit = 0 // 设置为0表示不限制选择数量,我们在代码中处理
|
||||
configuration.preferredAssetRepresentationMode = .current
|
||||
|
||||
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)))")
|
||||
}
|
||||
let picker = PHPickerViewController(configuration: configuration)
|
||||
picker.delegate = context.coordinator
|
||||
context.coordinator.currentPicker = picker
|
||||
|
||||
case .video(let url, _):
|
||||
print("Type: Video")
|
||||
print("File Name: \(url.lastPathComponent)")
|
||||
print("File Path: \(url.path)")
|
||||
return picker
|
||||
}
|
||||
|
||||
if let attributes = try? FileManager.default.attributesOfItem(atPath: url.path),
|
||||
let fileSize = attributes[.size] as? Int64 {
|
||||
print("File Size: \(formatFileSize(fileSize))")
|
||||
}
|
||||
func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {
|
||||
// 更新视图控制器(如果需要)
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator(self)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
@ -25,7 +25,8 @@ struct MediaUploadDemo: View {
|
||||
.sheet(isPresented: $showMediaPicker) {
|
||||
MediaPicker(
|
||||
selectedMedia: $uploadManager.selectedMedia,
|
||||
selectionLimit: 10,
|
||||
imageSelectionLimit: 2,
|
||||
videoSelectionLimit: 2,
|
||||
onDismiss: {
|
||||
showMediaPicker = false
|
||||
// 当媒体选择器关闭时,如果有选中的媒体,开始上传
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user