I'm populating a collection view with URLs loaded from Firebase. But I can't get the video to pause when I exit the view or scroll the collection view up. When I go back using the navigation controller, I can still hear the video playing in the background. Then when I enter the view again, the video starts playing, but the first one never finished.
This is my view controller. Any tips? thank you! I'm still new to Swift, so please excuse any ignorance on my end.
import UIKit
import AVKit
import AVFoundation
import Firebase
import FirebaseDatabase
import SDWebImage
class ComedyViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
var avPlayer = AVPlayer()
var avPlayerLayer = AVPlayerLayer()
@IBOutlet weak var comedyCollectionView: UICollectionView!
var comedyVideoArray = [ComedyModel]()
var comedyDBRef: DatabaseReference! {
return Database.database().reference().child("comedy")
}
override func viewDidLoad() {
super.viewDidLoad()
loadComedyDB()
}
func loadComedyDB() {
comedyDBRef.observe(DataEventType.value, with: { (snapshot) in
if snapshot.childrenCount > 0 {
self.comedyVideoArray.removeAll()
for comedyData in snapshot.children.allObjects as! [DataSnapshot] {
let comedyObject = comedyData.value as? [String: AnyObject]
let comedyPostTitle = comedyObject?["title"]
let comedyPostDescription = comedyObject?["description"]
let comedyArticleLink = comedyObject?["link"]
let comedyVideoUrl = comedyObject?["url"]
let allComedyData = ComedyModel(title: comedyPostTitle as! String?, description: comedyPostDescription as! String?, link: comedyArticleLink as! String?, url: comedyVideoUrl as! String?)
self.comedyVideoArray.append(allComedyData)
self.comedyCollectionView.reloadData()
}
}
})
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print(comedyVideoArray.count)
return comedyVideoArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cellB = comedyCollectionView.dequeueReusableCell(withReuseIdentifier: "comedyCell", for: indexPath) as! ComedyCollectionViewCell
let data = comedyVideoArray[indexPath.row]
let item = AVPlayerItem(url: URL(string: data.url!)!)
self.avPlayer = AVPlayer(playerItem: item)
self.avPlayer.actionAtItemEnd = .none
self.avPlayerLayer = AVPlayerLayer(player: self.avPlayer)
self.avPlayerLayer.videoGravity = .resizeAspectFill
self.avPlayerLayer.frame = CGRect(x: 0, y: 0, width: cellB.frame.size.width, height: cellB.frame.size.height / 2)
cellB.videoView.layer.addSublayer(self.avPlayerLayer)
self.avPlayer.play()
//cellB.videoView.sd_setImage(with: URL(string: data.link!), placeholderImage: UIImage(named: "1"))
return cellB
}
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if collectionView == self.comedyCollectionView {
self.avPlayer.pause()
}
}
}
There're several issues:
UICollectionView
calls cellForItemAt
before it shows new cell which means that old cell is still visible. Now, when you dequeue new cell, you are changing your pointer avPlayer
to the new instance of AVPlayer
so that when you're pausing the video you're actually calling pause()
on a new video. The old one continues to play (in a cell that is still visible at the time).
You're adding an instance of AVPlayer
to the cell each time UICollectionView
calls cellForItemAt
. But UICollectionView
tries to reuse those cells which are no longer visible so you can end up with a lot of AVPlayer
s in each cell. To avoid that you should check out how to use prepareForReuse
method on cells. I also suggest you to create AVPlayer
in cell itself (by subclassing the UICollectionViewCell
) and then set its playerItem
in cellForItemAt
method. This way you could pause currently playing video in your didEndDisplaying
method like this (just an example):
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if let comedyCell = cell as? ComedyCollectionViewCell {
comedyCell.avPlayer.pause()
}
}