Search code examples
iosswiftvideouicollectionviewinline

How do I play videos inline in a UICollectionView Swift?


A UICollectionView will consist of a feed of videos. When the user is inline with a video, I would like it to play. With my current setup, several videos play at once (I suppose depending on the pagination) once they are loaded in to the collection view.

How do I play videos inline in a UICollectionView?

A cell in the UICollectionView feed will contain a UIView, which will hold the video player. This is the UIView's class PlayerViewClass:

import Foundation
import UIKit
import AVKit
import AVFoundation

class PlayerViewClass: UIView {

    override static var layerClass: AnyClass {
        return AVPlayerLayer.self
    }

    var playerLayer: AVPlayerLayer {
    
        return layer as! AVPlayerLayer
    }

    var player: AVPlayer? {
        get {
            return playerLayer.player
        }
    
        set {
            playerLayer.player = newValue
        }
    }
}

The Feed's collectionView cellForItemAt indexPath delegate method in the FeedViewController is as follows:

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

     let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
        
      if let cell = cell as? MyCollectionViewCell {

      ...

      //Configuring the cell

      ...
    
      //Video player
      let avPlayer = AVPlayer(url: post.fullURL)
        
      //Setting cell's player
      cell.playerView.playerLayer.player = avPlayer
  
      //TODO: Change so this is only executed when the user is inline.
      cell.playerView.player?.play()
    
    }
    return cell
  }

The cell MyCollectionViewCell has an IBOutlet linked to the playerView UIView:

class MyCollectionViewCell {

    @IBOutlet weak var playerView: PlayerViewClass!


override func awakeFromNib() {
    super.awakeFromNib() 

   //Setup, not relevant

   }

}

I found the following GitHub repo, which shows the functionality that I would like to implement; however, I'm a little unsure of how to do this with my setup below.

Thanks so much!


Solution

  • You'll need to know which cells are visible.You get that "for free" via the UICollectionView API's visibleCells property. Here's the documentation.

    Additionally, you'll need to do some bookkeeping when the UICollectionView scrolls. In reviewing the documentation for UICollectionView, you'll see it is a subclass of UIScrollView. UIScrollView comes with delegate methods that enable you to track this. Here's a "physics for poets" approach to what you could do to accomplish this task:

    Let's say this is your view controller:

    class YourViewController: UIViewController {
        let collectionView = UICollectionView()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            collectionView.delegate = self
            collectionView.dataSource = self
        }
    }
    

    Then, you'd implement your UICollectionViewDelegate and UICollectionViewDataSource methods here:

    extension YourViewController: UICollectionViewDelegate, UICollectionViewDataSource {
        // Bare bones implementation
        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            // TODO: need to implement
            return 0
        }
    
        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            // TODO: need to implem,ent
            return UICollectionViewCell()
        }
    }
    

    Finally, you'd implement UIScrollViewDelegate methods to detect beginning of scrolling and end of scrolling. Here's where you'd implement your logic for starting/stopping video:

    extension YourViewController: UIScrollViewDelegate {
        func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
            collectionView.visibleCells.forEach { cell in
                // TODO: write logic to stop the video before it begins scrolling
            }
        }
    
        func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
            collectionView.visibleCells.forEach { cell in
                // TODO: write logic to start the video after it ends scrolling
            }
        }
    }
    

    You'll want to muck around with the timing of stopping/starting animations to see what looks good, so feel free to poke around the various UIScrollViewDelegate methods.