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

Swift实现UICollectionView视频预加载缓存 优化点击播放体验

iOS UICollectionView 视频预加载优化方案

现有点击跳转后才加载视频的逻辑,本质是资源加载时机太晚,把加载时机提前到列表滑动阶段、配合缓存复用就能解决点击后等待过长的问题,具体实现步骤如下:

  • 搭建轻量视频资源缓存池
    用系统自带的NSCache做内存级缓存即可,key用视频URL的字符串标识,value存储预加载完成的AVURLAsset实例,不要缓存AVPlayer这类和页面layer绑定的重资源。NSCache会在系统内存不足时自动释放闲置缓存,不需要额外写太多内存监听的清理逻辑。可以把缓存逻辑抽成单例管理者,方便列表页和播放页共同访问。
  • 利用系统预加载协议触发提前加载
    不需要自己计算滑动速度、预判滑动方向,直接用UICollectionView自带的UICollectionViewDataSourcePrefetching协议,系统会自动识别用户接下来大概率会浏览到的cell位置,在回调里触发对应视频资源的预加载:
    // 列表页实现预加载代理
    extension MomentListViewController: UICollectionViewDataSourcePrefetching {
        func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
            for indexPath in indexPaths {
                // 越界校验
                guard indexPath.item < dataSource.count,
                      let moment = dataSource[indexPath.item] as? Moment,
                      let urlStr = moment.videoUrl,
                      let videoUrl = URL(string: urlStr) else { continue }
                // 已经缓存过的资源跳过,不重复加载
                if VideoCacheManager.shared.containAsset(for: urlStr) { continue }
                // 初始化资源,只加载播放必需的关键属性
                let asset = AVURLAsset(url: videoUrl)
                asset.loadValuesAsynchronously(forKeys: ["playable", "tracks"]) {
                    var keyLoadError: NSError?
                    let loadStatus = asset.statusOfValue(forKey: "playable", error: &keyLoadError)
                    // 加载成功才存入缓存
                    if loadStatus == .loaded, keyLoadError == nil {
                        VideoCacheManager.shared.storeAsset(asset, for: urlStr)
                    }
                }
            }
        }
        
        // 滑出预加载范围的任务直接取消,节省带宽
        func collectionView(_ collectionView: UICollectionView, cancelPrefetchingForItemsAt indexPaths: [IndexPath]) {
            for indexPath in indexPaths {
                guard indexPath.item < dataSource.count,
                      let moment = dataSource[indexPath.item] as? Moment,
                      let urlStr = moment.videoUrl else { continue }
                VideoCacheManager.shared.cancelLoad(for: urlStr)
            }
        }
    }
    
    记得给列表开启预加载能力:collectionView.prefetchingEnabled = true,同时设置collectionView.prefetchDataSource = self。
  • 改造播放页初始化逻辑,优先命中缓存
    跳转播放页时把对应视频的URL参数传入,初始化播放器时优先从缓存池取预加载完成的资源生成播放项,没有命中缓存再走原始的URL加载逻辑做兜底。改造后的configureVideo方法参考:
    fileprivate func configureVideo(with moment : Moment){
        guard let urlString = moment.videoUrl else { return }
        guard let videoUrl = URL(string: urlString) else { return }
        
        let playerItem: AVPlayerItem
        // 优先使用预加载完成的缓存资源
        if let cachedAsset = VideoCacheManager.shared.getAsset(for: urlString) {
            playerItem = AVPlayerItem(asset: cachedAsset)
        } else {
            // 缓存未命中走原始加载逻辑兜底
            playerItem = AVPlayerItem(url: videoUrl)
        }
        
        self.player = AVPlayer(playerItem: playerItem)
        
        // 原有的KVO、通知、图层配置逻辑保留
        self.player!.addObserver(self, forKeyPath: #keyPath(player.currentItem.isPlaybackLikelyToKeepUp),
                                 options: .new, context: &playbackLikelyToKeepUpContext)
        
        NotificationCenter.default.addObserver(self, selector: #selector(videoReachedEnd), name: .AVPlayerItemDidPlayToEndTime, object: self.player?.currentItem)
        
        self.playerLayer = AVPlayerLayer(player: player)
        self.view.layer.insertSublayer(playerLayer!, at: 0)
        self.playerLayer?.frame = self.view.bounds
        self.player?.play()
    }
    
  • 补充必要的优化逻辑
    • 可以根据网络状态调整预加载策略:WiFi环境下按系统返回的预加载范围全量加载,蜂窝网络下只预加载当前可见cell前后1-2条的视频,避免消耗过多用户流量。
    • 原播放页代码缺少观察者移除逻辑,记得在deinit方法中移除KVO和对应通知,避免内存泄漏。
    • 如果需要实现二次启动不重复加载的持久化缓存,可以通过AVAssetResourceLoader自定义资源加载代理,把下载完成的视频数据存入沙盒,后续相同URL直接读本地文件即可,内存级预加载已经能解决点击跳转的等待问题,持久化缓存可以后续迭代再加。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 02:30:58