You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过loadTransferable捕获视频处理进度并在UI展示?

问题根源

你当前的实现无法捕获进度的核心原因有两个:

  1. FileManager.copyItem(at:to:)是同步无进度回调的方法,不会向loadTransferable返回的Progress实例传递任何进度数据
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 19:12:23