wake-ios/wake/View/Components/Upload/ImageMultiUploader.swift

176 lines
6.7 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import SwiftUI
import PhotosUI
import os
@available(iOS 16.0, *)
public struct MultiImageUploader<Content: View>: View {
@State var selectedImages: [UIImage] = []
@State private var uploadResults: [UploadResult] = []
@State private var isUploading = false
@State private var showingImagePicker = false
@State private var uploadProgress: [UUID: Double] = [:] //
@State private var needsViewUpdate = false // Add this line to trigger view updates
private let maxSelection: Int
public var onUploadComplete: ([UploadResult]) -> Void
private let uploadService = ImageUploadService.shared
private let logger = Logger(subsystem: "com.yourapp.uploader", category: "MultiImageUploader")
//
private let content: ((_ isUploading: Bool, _ selectedCount: Int) -> Content)?
///
@Binding var isImagePickerPresented: Bool
///
@Binding var selectedImagesBinding: [UIImage]?
///
@State private var showingImagePreview = false
// - 使
public init(
maxSelection: Int = 10,
isImagePickerPresented: Binding<Bool>,
selectedImagesBinding: Binding<[UIImage]?>,
@ViewBuilder content: @escaping (_ isUploading: Bool, _ selectedCount: Int) -> Content,
onUploadComplete: @escaping ([UploadResult]) -> Void
) {
self.maxSelection = maxSelection
self._isImagePickerPresented = isImagePickerPresented
self._selectedImagesBinding = selectedImagesBinding
self.content = content
self.onUploadComplete = onUploadComplete
}
// - 使
public init(
maxSelection: Int = 10,
isImagePickerPresented: Binding<Bool>,
selectedImagesBinding: Binding<[UIImage]?>,
onUploadComplete: @escaping ([UploadResult]) -> Void
) where Content == EmptyView {
self.maxSelection = maxSelection
self._isImagePickerPresented = isImagePickerPresented
self._selectedImagesBinding = selectedImagesBinding
self.content = nil
self.onUploadComplete = onUploadComplete
}
public var body: some View {
VStack(spacing: 16) {
//
if let content = content {
Button(action: {
showingImagePicker = true
}) {
content(isUploading, selectedImages.count)
}
.buttonStyle(PlainButtonStyle())
} else {
//
Button(action: {
showingImagePicker = true
}) {
Label(
!selectedImages.isEmpty ?
"已选择 \(selectedImages.count) 张图片" :
"选择图片",
systemImage: "photo.on.rectangle"
)
.font(.headline)
.foregroundColor(.white)
.padding()
.frame(maxWidth: .infinity)
.background(Color.blue)
.cornerRadius(10)
}
.padding(.horizontal)
}
}
.sheet(isPresented: $showingImagePicker) {
ImagePicker(images: $selectedImages, selectionLimit: maxSelection) {
//
showingImagePicker = false
if !selectedImages.isEmpty {
Task {
_ = await startUpload()
}
}
}
}
.onChange(of: isImagePickerPresented) { _, newValue in
if newValue {
showingImagePicker = true
}
}
.onChange(of: showingImagePicker) { _, newValue in
if !newValue {
isImagePickerPresented = false
}
}
.onChange(of: selectedImages) { _, newValue in
selectedImagesBinding = newValue
}
.onChange(of: needsViewUpdate) { _, _ in
// Trigger view update
}
}
///
@MainActor
public func startUpload() async -> [UploadResult] {
guard !isUploading && !selectedImages.isEmpty else { return [] }
isUploading = true
uploadResults = selectedImages.map {
UploadResult(image: $0, status: .uploading(progress: 0))
}
let group = DispatchGroup()
for (index, image) in selectedImages.enumerated() {
group.enter()
// 使 ImageUploadService
uploadService.uploadOriginalAndCompressedImage(
image,
compressionQuality: 0.7,
progress: { progress in
//
DispatchQueue.main.async {
if index < self.uploadResults.count {
self.uploadResults[index].status = .uploading(progress: progress.progress)
self.needsViewUpdate.toggle() // Trigger view update
}
}
},
completion: { result in
DispatchQueue.main.async {
guard index < self.uploadResults.count else { return }
switch result {
case .success(let uploadResults):
self.uploadResults[index].status = .success
self.uploadResults[index].fileId = uploadResults.original.fileId
self.uploadResults[index].previewFileId = uploadResults.compressed.fileId
self.needsViewUpdate.toggle() // Trigger view update
case .failure(let error):
self.uploadResults[index].status = .failure(error)
self.needsViewUpdate.toggle() // Trigger view update
self.logger.error("图片上传失败: \(error.localizedDescription)")
}
group.leave()
}
}
)
}
return await withCheckedContinuation { continuation in
group.notify(queue: .main) {
self.isUploading = false
self.needsViewUpdate.toggle() // Trigger view update
continuation.resume(returning: self.uploadResults)
}
}
}
}