如何通过loadTransferable捕获视频处理进度并在UI展示?
问题根源
你当前的实现无法捕获进度的核心原因有两个:
FileManager.copyItem(at:to:)是同步无进度回调的方法,不会向loadTransferable返回的Progress实例传递任何进度数据loadTransferable返回的Progress仅用于标记任务开始/结束,不会自动跟踪文件复制这类自定义操作的进度
修复方案
我们需要手动实现带进度跟踪的文件复制逻辑,替换原有的同步复制代码,同时自己维护进度状态:
1. 更新状态属性
替换原有的videoProcessing,改用可手动控制的Progress实例:
@State private var videoProcessingProgress = Progress(totalUnitCount: 100) @State private var isVideoProcessing = false @State private var pickedVideoUrl: URL?
2. 替换文件导入逻辑
放弃loadTransferable的自动导入,改用后台任务手动处理文件复制并跟踪进度:
// 替换你原来的捕获进度代码 isVideoProcessing = true Task { guard let receivedFile = newValue.file as? ReceivedTransferredFile else { await MainActor.run { isVideoProcessing = false } return } let originalFile = receivedFile.file let copiedFile = URL.documentsDirectory.appending(path: "videoPicker.mov") // 清理旧文件 if FileManager.default.fileExists(atPath: copiedFile.path()) { try? FileManager.default.removeItem(at: copiedFile) } do { // 获取源文件总大小,作为进度计算基准 let fileSize = try originalFile.resourceValues(forKeys: [.fileSizeKey]).fileSize ?? 0 await MainActor.run { videoProcessingProgress.totalUnitCount = Int64(fileSize) videoProcessingProgress.completedUnitCount = 0 } // 用FileHandle分块复制,实时跟踪进度 let inputHandle = try FileHandle(forReadingFrom: originalFile) let outputHandle = try FileHandle(forWritingTo: copiedFile) defer { inputHandle.closeFile() outputHandle.closeFile() await MainActor.run { isVideoProcessing = false } } let bufferSize = 1024 * 1024 // 1MB分块,可根据需求调整 var totalBytesCopied: Int64 = 0 while true { let data = inputHandle.readData(ofLength: bufferSize) guard !data.isEmpty else { break } outputHandle.write(data) totalBytesCopied += Int64(data.count) // 主线程更新进度(@State必须在主线程修改) await MainActor.run { videoProcessingProgress.completedUnitCount = totalBytesCopied } } // 复制完成后更新视频URL await MainActor.run { pickedVideoUrl = copiedFile } } catch { await MainActor.run { isVideoProcessing = false } print("文件复制失败:\(error)") } }
3. 更新ProgressView UI
直接使用Progress的fractionCompleted属性,简化进度计算:
if isVideoProcessing { ProgressView(value: videoProcessingProgress.fractionCompleted) .frame(width: proxy.size.width, height: 50) .progressViewStyle(.linear) }
关键注意事项
- 文件复制必须放在后台线程执行,避免阻塞主线程导致UI卡顿
- 修改
@State属性时必须切换到主线程,符合SwiftUI的线程安全要求 - 分块大小可根据实际场景调整(比如小文件用更小的块,大文件用更大的块)
内容的提问来源于stack exchange,提问作者Sergey Udalov
相关产品推荐
相关产品推荐

