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

关于.loadTransferable(type:completion:)进度追踪失效的问题咨询

问题描述

使用PhotosPickerItem的loadTransferable(type:completionHandler:)方法返回的NSProgress对象无法正常更新:

  • KVO无法触发进度变更通知
  • 用定时器轮询观察,日志始终输出如下内容,进度无任何变化:
    <NSProgress: 0x600002c2d300> : Parent: 0x0 (portion: 0) / Fraction completed: 0.0000 / Completed: 0 of 0
    

相关代码:

private func onSelection() {
    guard let item = self.selection else {
        return
    }
    
    self.progress = item.loadTransferable(type: Data.self) { result in
        /// ...
    }
    
    self.observeProgress()
}

private func observeProgress() {
    guard let progress = self.progress else { return }
    let _ = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { timer in
        print(progress)
        
        if progress.isFinished {
            timer.invalidate()
        }
    }
}
解决方案

1. 先确认进度对象的触发条件

loadTransferable返回的NSProgress只有在资源需要从iCloud下载时才会逐步更新进度。如果资源已经在本地缓存,任务会直接完成,进度对象可能不会有中间状态的变化。

可以先在completion回调里确认传输状态:

self.progress = item.loadTransferable(type: Data.self) { result in
    switch result {
    case .success(let data):
        print("传输完成,数据大小:\(data?.count ?? 0)")
    case .failure(let error):
        print("传输失败:\(error.localizedDescription)")
    }
}

2. 正确用KVO观察进度

如果确实需要监听进度,要保证KVO的实现符合规范:

  • 必须在主线程添加观察者(NSProgress的KVO通知默认在主线程发送)
  • 正确实现observeValue(forKeyPath:of:change:context:)方法
  • 记得在任务结束后移除观察者,避免内存泄漏

示例代码:

private var progressObservationContext = 0

private func onSelection() {
    guard let item = self.selection else { return }
    
    self.progress = item.loadTransferable(type: Data.self) { [weak self] result in
        // 处理结果后移除观察者
        self?.progress?.removeObserver(self!, forKeyPath: "fractionCompleted", context: &self!.progressObservationContext)
    }
    
    progress?.addObserver(self, forKeyPath: "fractionCompleted", options: [.initial, .new], context: &progressObservationContext)
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    guard context == &progressObservationContext else {
        super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
        return
    }
    
    if keyPath == "fractionCompleted", let progress = object as? NSProgress {
        print("当前进度:\(progress.fractionCompleted)")
        if progress.isFinished {
            progress.removeObserver(self, forKeyPath: "fractionCompleted", context: &progressObservationContext)
        }
    }
}

3. 用Combine替代KVO(Swift 5.1+)

如果项目使用Combine框架,监听进度会更简洁:

import Combine

private var cancellables = Set<AnyCancellable>()

private func onSelection() {
    guard let item = self.selection else { return }
    
    self.progress = item.loadTransferable(type: Data.self) { result in
        // 处理结果
    }
    
    progress?.publisher(for: \.fractionCompleted)
        .receive(on: DispatchQueue.main)
        .sink { [weak self] fraction in
            print("当前进度:\(fraction)")
            if self?.progress?.isFinished == true {
                self?.cancellables.removeAll()
            }
        }
        .store(in: &cancellables)
}

4. 检查相册权限配置

确保Info.plist中添加了必要的权限声明:

  • NSPhotoLibraryUsageDescription(访问本地相册权限)
  • NSPhotoLibraryAddUsageDescription(保存到相册权限)

内容的提问来源于stack exchange,提问作者sadel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 04:44:57