在 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?