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

PhotosPickerItem的loadTransferable进度监听失效,求解决方案

解决PhotosPickerItem.loadTransferable进度监听失效问题

核心问题分析

你遇到的监听闭包不执行,主要是两个原因:

  1. Progress对象生命周期过短:原代码中progress是局部变量,processSelectedVideo方法执行完毕后,progress会被系统释放,导致KVO监听失去目标。
  2. 线程调度问题:Progress的进度更新默认在后台线程触发,若未指定主线程队列,回调可能无法正常执行或无法更新UI。

解决方案一:用ObservableObject持有Progress并修复KVO监听

通过ObservableObject统一管理加载状态和Progress对象,确保其生命周期与View绑定,同时指定主线程执行回调。

import SwiftUI
import Photos
import PhotosUI

class VideoLoader: ObservableObject {
    @Published var progressValue: Double = 0.0
    private var progress: Progress?
    private var observation: NSKeyValueObservation?
    
    func loadVideo(from item: PhotosPickerItem) {
        // 清理之前的监听
        observation?.invalidate()
        observation = nil
        progress = nil
        
        progress = item.loadTransferable(type: Data.self) { [weak self] result in
            guard let self = self else { return }
            
            // 处理加载结果
            switch result {
            case .success(let data):
                if let data = data {
                    // 这里处理视频数据,比如转存到本地或解码
                }
            case .failure(let error):
                print("视频加载失败: \(error.localizedDescription)")
            }
            
            // 加载完成后清理资源
            self.observation?.invalidate()
            self.observation = nil
            self.progress = nil
        }
        
        guard let progress = progress else { return }
        // 指定主线程队列执行回调,确保UI更新正常
        observation = progress.observe(\.fractionCompleted, options: [.new], queue: .main) { [weak self] _, change in
            if let newValue = change.newValue {
                self?.progressValue = newValue
                print("当前加载进度: \(newValue)")
            }
        }
        progress.resume()
    }
}

struct ContentView: View {
    @State private var selectedVideo: PhotosPickerItem?
    @StateObject private var videoLoader = VideoLoader()
    
    var body: some View {
        VStack(spacing: 20) {
            PhotosPicker(selection: $selectedVideo, matching: .videos) {
                Text("选择相册视频")
                    .padding()
                    .background(Color.blue)
                    .foregroundColor(.white)
                    .cornerRadius(8)
            }
            
            ProgressView(value: videoLoader.progressValue)
                .frame(height: 8)
                .padding(.horizontal, 20)
        }
        .onChange(of: selectedVideo) { newItem in
            if let item = newItem {
                videoLoader.loadVideo(from: item)
            }
        }
    }
}

解决方案二:使用Combine监听Progress(更符合SwiftUI响应式风格)

利用Combine框架监听Progress的属性变化,替代KVO,代码更简洁且更适配SwiftUI生态。

只需修改VideoLoader类,添加Combine订阅逻辑:

import Combine

class VideoLoader: ObservableObject {
    @Published var progressValue: Double = 0.0
    private var progress: Progress?
    private var cancellable: AnyCancellable?
    
    func loadVideo(from item: PhotosPickerItem) {
        // 清理之前的订阅
        cancellable?.cancel()
        cancellable = nil
        progress = nil
        
        progress = item.loadTransferable(type: Data.self) { [weak self] result in
            guard let self = self else { return }
            // 处理加载结果逻辑同上
            self.cancellable?.cancel()
            self.cancellable = nil
            self.progress = nil
        }
        
        guard let progress = progress else { return }
        // 通过Combine监听进度变化,切换到主线程更新UI
        cancellable = progress.publisher(for: \.fractionCompleted)
            .receive(on: DispatchQueue.main)
            .sink { [weak self] value in
                self?.progressValue = value
                print("当前加载进度: \(value)")
            }
        progress.resume()
    }
}

关键优化点说明

  • 持有Progress对象:通过ObservableObject的属性持有Progress,避免局部变量被提前释放。
  • 主线程调度:无论是KVO还是Combine,都指定主线程执行回调,确保进度值能正确更新UI。
  • 资源清理:在加载完成或重新加载时,及时销毁旧的监听/订阅,避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 11:42:49