基本上我想做的是缓存单元格并让视频继续播放。当用户滚动回到单元格时,视频应该只从播放的位置显示。
问题是玩家被移除并且单元格最终出现在随机单元格上,而不是其指定区域。
您需要有两个视频才能正常工作,我从这里下载了视频https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4 https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4我刚刚用两个不同的名称保存了同一个视频两次。
如何复制问题:
点击第一个单元格,然后一直向下滚动,然后向上滚动,您会注意到视频开始出现在各处。我只想让视频出现在正确的位置,而不是出现在其他地方。
这是代码的链接:代码链接 https://github.com/Abstract45/QuestionTest
class ViewController: UIViewController {
private var collectionView: UICollectionView!
private var videosURLs: [String] = [
"ElephantsDream2", "ElephantsDream", "ElephantsDream", "ElephantsDream", "ElephantsDream", "ElephantsDream", "ElephantsDream", "ElephantsDream", "ElephantsDream", "ElephantsDream", "ElephantsDream", "ElephantsDream", "ElephantsDream", "ElephantsDream",
"ElephantsDream", "ElephantsDream", "ElephantsDream", "ElephantsDream",
"ElephantsDream", "ElephantsDream", "ElephantsDream", "ElephantsDream",
"ElephantsDream", "ElephantsDream", "ElephantsDream", "ElephantsDream"
]
var cacheItem = [String: (cell: CustomCell, player: AVPlayer)]()
override func viewDidLoad() {
super.viewDidLoad()
setupCollectionView()
}
private func setupCollectionView() {
collectionView = UICollectionView(frame: .zero, collectionViewLayout: ColumnFlowLayout())
view.addSubview(collectionView)
collectionView.register(CustomCell.self, forCellWithReuseIdentifier: "cell")
collectionView.dataSource = self
collectionView.delegate = self
collectionView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
collectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
collectionView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
])
}
}
extension ViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard let cell = collectionView.cellForItem(at: indexPath) as? CustomCell else { return }
let item = videosURLs[indexPath.row]
let viewModel = PlayerViewModel(fileName: item)
cell.setupPlayerView(viewModel.player)
cacheItem[item] = (cell, viewModel.player)
}
}
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
videosURLs.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let item = videosURLs[indexPath.row]
if let cachedItem = cacheItem[item], indexPath.row == 0 {
print(indexPath)
print(item)
cachedItem.cell.setUpFromCache(cachedItem.player)
return cachedItem.cell
} else {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? CustomCell else { return UICollectionViewCell() }
cell.contentView.backgroundColor = .orange
let url = Bundle.main.url(forResource: item, withExtension: "mp4")
cell.playerItem = AVPlayerItem(url: url!)
return cell
}
}
}
class CustomCell: UICollectionViewCell {
private var cancelBag: Set<AnyCancellable> = []
private(set) var playerView: PlayerView?
var playerItem: AVPlayerItem?
override var reuseIdentifier: String?{
"cell"
}
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
override func prepareForReuse() {
super.prepareForReuse()
playerView = nil
playerView?.removeFromSuperview()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupViews() {
layer.cornerRadius = 8
clipsToBounds = true
}
func setUpFromCache(_ player: AVPlayer) {
playerView?.player = player
}
func setupPlayerView(_ player: AVPlayer) {
if self.playerView == nil {
self.playerView = PlayerView(player: player, gravity: .aspectFill)
contentView.addSubview(playerView!)
playerView?.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
playerView!.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
playerView!.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
playerView!.topAnchor.constraint(equalTo: contentView.topAnchor),
playerView!.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
])
playerView?.player?.play()
NotificationCenter.default.publisher(for: .AVPlayerItemDidPlayToEndTime).sink { [weak self] notification in
if let p = notification.object as? AVPlayerItem, p == player.currentItem {
self?.playerView?.removeFromSuperview()
guard let self = self else { return }
NotificationCenter.default.removeObserver(self, name: .AVPlayerItemDidPlayToEndTime, object: nil)
}
}.store(in: &cancelBag)
} else {
playerView?.player?.pause()
playerView?.removeFromSuperview()
playerView = nil
}
}
}