在 UICollectionViewCell 中播放视频。视频播放正常,但在 collectionView 上有滚动性能

Posted

技术标签:

【中文标题】在 UICollectionViewCell 中播放视频。视频播放正常,但在 collectionView 上有滚动性能【英文标题】:Playing videos in UICollectionViewCell. Video is playing fine but have scroll performance on collectionView 【发布时间】:2017-06-09 06:33:12 【问题描述】:

我在社区上探索了其他答案并尝试了这个 -

    player = AVPlayer()
    player.volume = 0.0
    player.actionAtItemEnd = AVPlayerActionAtItemEnd.None
    addPlayerLayer()

    let asset = AVAsset.init(URL: videoURL)
    asset.loadValuesAsynchronouslyForKeys(["duration", "playable"]) 

        dispatch_async(dispatch_get_main_queue(),  () -> Void in
                let item = AVPlayerItem.init(asset: asset)
                self.player.replaceCurrentItemWithPlayerItem(item)
                self.player.play()
        )
    

但是,只要网络速度较慢,滚动 collectionView 时就会出现明显的延迟/打嗝。难道我做错了什么?请帮忙

【问题讨论】:

AVPlayerViewController in UICollectionViewCell bug?的可能重复 不是重复的,是关于collectionView在播放视频时的滚动性能。 你是怎么做到的?我有一堆来自服务器的视频需要在收藏视图中水平播放.. @FaridAlHaddad 最后,我首先播放可见视频,然后在滚动时播放视频滚动停止(只有那些可见的视频)。这没有滞后。如果此策略适合您的要求并且您需要代码方面的帮助,请告诉我,我可以发布此问题的答案。 是的,这对我有用,你能告诉我你是怎么做到的吗?另外,我认为每次滚动视频都会从互联网再次加载消耗大量数据,你知道我该如何处理视频的缓存吗?谢谢你,密歇根 【参考方案1】:

请查看 Facebook 的 AsyncDisplayKit(Facebook 和 Instagram 提要背后的引擎),您可以使用他们的 AVideoNode 在后台线程上渲染大部分视频。

或者:

player = AVPlayer()
player.volume = 0.0
player.actionAtItemEnd = AVPlayerActionAtItemEnd.None
addPlayerLayer()
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), 
    let asset = AVAsset.init(URL: videoURL)
    asset.loadValuesAsynchronouslyForKeys(["duration", "playable"]) 
        dispatch_async(dispatch_get_main_queue(),  () -> Void in
                let item = AVPlayerItem.init(asset: asset)
                self.player.replaceCurrentItemWithPlayerItem(item)
                self.player.play()
        )
    
)

【讨论】:

我试过你的代码和facebook库,都没有解决滚动延迟问题。【参考方案2】:

这是一个示例,如何在 collectionView 单元格中播放视频而不会出现滚动延迟。

首先,我们必须在单元格的 init 方法中准备好播放视频所需的一切,而不添加项目或资产,然后我们可以在异步分配视频 url 或路径时创建资产或项目。

第二个重要的事情是将视频大小调整为单元格大小乘以 2(例如,我的单元格大小为 (w:50, h:50),我将视频大小调整为 (w:100, h:100) 以获得质量好)。

在此示例中,我将使用 AVPlayerLooper 自动循环播放视频,以便您可以根据需要使用 AVPlayer 进行更改。

CollectionViewCell 类示例:

import UIKit
import AVKit

class ExampleCell: UICollectionViewCell 

    public var isPlaying: Bool = false
    public var videolink: URL? = nil 
        didSet 
            guard let link = videolink, oldValue != link else  return 
            loadVideoUsingURL(link)
        
    
    private var queuePlayer = AVQueuePlayer()
    private var playerLayer = AVPlayerLayer()
    private var looperPlayer: AVPlayerLooper?
    
    override init(frame: CGRect) 
        super.init(frame: frame)
        
        commonInit()
    
    
    required init?(coder aDecoder: NSCoder) 
        super.init(coder: aDecoder)
        
        commonInit()
    
    
    private func commonInit() 
        
        queuePlayer.volume = 0.0
        queuePlayer.actionAtItemEnd = .none
        
        playerLayer.videoGravity = .resizeAspect
        playerLayer.name = "videoLoopLayer"
        playerLayer.cornerRadius = 5.0
        playerLayer.masksToBounds = true
        contentView.layer.addSublayer(playerLayer)
        playerLayer.player = queuePlayer
    
    
    override func layoutSubviews() 
        super.layoutSubviews()
        
        /// Resize video layer based on new frame
        playerLayer.frame = CGRect(origin: .zero, size: CGSize(width: frame.width, height: frame.width))
    
    
    private func loadVideoUsingURL(_ url: URL) 
        /// Load asset in background thread to avoid lagging
        DispatchQueue.global(qos: .background).async 
            let asset = AVURLAsset(url: url)
            /// Load needed values asynchronously
            asset.loadValuesAsynchronously(forKeys: ["duration", "playable"]) 
                /// UI actions should executed on the main thread
                DispatchQueue.main.async  [weak self] in
                    guard let `self` = self else  return 
                    let item = AVPlayerItem(asset: asset)
                    if self.queuePlayer.currentItem != item 
                        self.queuePlayer.replaceCurrentItem(with: item)
                        self.looperPlayer = AVPlayerLooper(player: self.queuePlayer, templateItem: item)
                    
                
            
        
    
    
    public func startPlaying() 
        queuePlayer.play()
        isPlaying = true
    
    
    public func stopPlaying() 
        queuePlayer.pause()
        isPlaying = false
    

而collectionView应该是这样实现的:

    实例化你的单元格

     func collectionView(_ collectionView: UICollectionView,
                        cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
    
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: ExampleCell.self),
                                                            for: indexPath) as? ExampleCell else 
            return UICollectionViewCell()
        
    
        cell.videolink = videoFileURL
        return cell
    
    

    collectionView 显示单元格时开始播放视频,结束显示时停止播放视频

    func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) 
        guard let videoCell = cell as? ExampleCell else  return 
        videoCell.startPlaying()
    
    func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) 
        guard let videoCell = cell as? ExampleCell else  return 
        videoCell.stopPlaying()
    
    

    处理滚动播放状态

    // TODO: write logic to stop the video before it begins scrolling
    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) 
       let cells = collectionView.visibleCells.compactMap( $0 as? ExampleCell )
       cells.forEach  videoCell in
    
           if videoCell.isPlaying 
               videoCell.stopPlaying()
           
       
    
    
    // TODO: write logic to start the video after it ends scrolling
    func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) 
       guard !decelerate else  return 
       let cells = collectionView.visibleCells.compactMap( $0 as? ExampleCell )
       cells.forEach   videoCell in
    
           if !videoCell.isPlaying && canPlayVideos 
               videoCell.startPlaying()
           
       
    
    
    // TODO: write logic to start the video after it ends scrolling (programmatically)
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) 
        let cells = collectionView.visibleCells.compactMap( $0 as? ExampleCell )
        cells.forEach  videoCell in
           // TODO: write logic to start the video after it ends scrolling
           if !videoCell.isPlaying && canPlayVideos 
               videoCell.startPlaying()
           
       
    
    

【讨论】:

以上是关于在 UICollectionViewCell 中播放视频。视频播放正常,但在 collectionView 上有滚动性能的主要内容,如果未能解决你的问题,请参考以下文章

在 UICollectionViewCell 中更新 UITableView

如何在 UICollectionViewCell 中添加 UICollectionView?

如何在 UICollectionViewCell 上设置 UILabel?

UICollectionViewCell 边框/阴影

如何在 iOS 7 中刷新 UICollectionViewCell?

UICollectionviewcell 中未显示数据?