关于.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
相关产品推荐
相关产品推荐

