title: How can I set up a collection view of AVPlayers to only play a video in the current selected cell?
body: <p>I have a UICollectionView setup that has cells of video posts from my database. Right now, when the collection view is loaded, all of the videos in the different cells start playing. I want the videos to not play in any cells except the selected cell so that the video audios don't play over each other. How can I do this? Here is the code...</p>
  <p>The view controller:</p>
  <pre><code>import UIKit
  import Photos
  
  struct VideoModel {
      let username: String
      let videoFileURL: String
  }
  
  class BetaClipsViewController: UIViewController, UICollectionViewDelegate {
      
      private var collectionView: UICollectionView?
      
      private var data = [VideoModel]()
      
      /// Notification observer
      private var observer: NSObjectProtocol?
      
      /// All post models
      private var allClips: [(clip: Clip, owner: String)] = []
      
      private var viewModels = [[ClipFeedCellType]]()
      
      override func viewDidLoad() {
          super.viewDidLoad()
          
          title = &quot;&quot;
          
          //        for _ in 0..&lt;10 {
          //            let model = VideoModel(username: &quot;@CJMJM&quot;,
          //                                   videoFileURL: &quot;https://firebasestorage.googleapis.com:443/v0/b/globe-e8b7f.appspot.com/o/clipvideos%2F1637024382.mp4?alt=media&amp;token=c12d0481-f834-4a17-8eee-30595bdf0e8b&quot;)
          //            data.append(model)
          //        }
          
          let layout = UICollectionViewFlowLayout()
          layout.scrollDirection = .horizontal
          layout.itemSize = CGSize(width: view.frame.size.width,
                                   height: view.frame.size.height)
          layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
          layout.minimumInteritemSpacing = 0
          layout.minimumLineSpacing = 0
          collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
          collectionView?.register(ClipsCollectionViewCell.self,
                                   forCellWithReuseIdentifier: ClipsCollectionViewCell.identifier)
          collectionView?.isPagingEnabled = true
          collectionView?.delegate = self
          collectionView?.dataSource = self
          view.addSubview(collectionView!)
          
          fetchClips()
          
          observer = NotificationCenter.default.addObserver(
              forName: .didPostNotification,
              object: nil,
              queue: .main
          ) { [weak self] _ in
              self?.viewModels.removeAll()
              self?.fetchClips()
          }
          self.collectionView?.reloadData()
      }
      
      override func viewDidLayoutSubviews() {
          super.viewDidLayoutSubviews()
          collectionView?.frame = view.bounds
      }
      
      private func fetchClips() {
          //        guard let username = UserDefaults.standard.string(forKey: &quot;username&quot;) else {
          //            return
          //        }
          let userGroup = DispatchGroup()
          userGroup.enter()
          
          var allClips: [(clip: Clip, owner: String)] = []
          
          
          DatabaseManager.shared.clips() { result in
              DispatchQueue.main.async {
                  defer {
                      userGroup.leave()
                  }
                  
                  switch result {
                  case .success(let clips):
                      allClips.append(contentsOf: clips.compactMap({
                          (clip: $0, owner: $0.owner)
                      }))
                      
                  case .failure:
                      break
                  }
              }
          }
          
          userGroup.notify(queue: .main) {
              let group = DispatchGroup()
              self.allClips = allClips
              allClips.forEach { model in
                  group.enter()
                  self.createViewModel(
                      model: model.clip,
                      username: model.owner,
                      completion: { success in
                          defer {
                              group.leave()
                          }
                          if !success {
                              print(&quot;failed to create VM&quot;)
                          }
                      }
                  )
              }
              
              group.notify(queue: .main) {
                  self.sortData()
                  self.collectionView?.reloadData()
              }
          }
      }
      
      private func sortData() {
          allClips = allClips.shuffled()
          
          viewModels = viewModels.shuffled()
      }
  }
  
  extension BetaClipsViewController: UICollectionViewDataSource {
      func numberOfSections(in collectionView: UICollectionView) -&gt; Int {
          return viewModels.count
      }
      
      func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -&gt; Int {
          return viewModels[section].count
      }
      
      func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -&gt; UICollectionViewCell {
          let cellType = viewModels[indexPath.section][indexPath.row]
          switch cellType {
          case .clip(let viewModel):
              guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ClipsCollectionViewCell.identifier,
                                                                  for: indexPath)
                      as? ClipsCollectionViewCell else {
                          fatalError()
                      }
              cell.delegate = self
              cell.configure(with: viewModel)
              return cell
          }
      }
  }
  
  extension BetaClipsViewController: ClipsCollectionViewCellDelegate {
      func didTapProfile(with model: VideoModel) {
          print(&quot;profile tapped&quot;)
          let owner = model.username
          DatabaseManager.shared.findUser(username: owner) { [weak self] user in
              DispatchQueue.main.async {
                  guard let user = user else {
                      return
                  }
                  let vc = ProfileViewController(user: user)
                  self?.navigationController?.pushViewController(vc, animated: true)
              }
          }
      }
      
      func didTapShare(with model: VideoModel) {
          print(&quot;tapped share&quot;)
      }
      
      func didTapNewClip(with model: VideoModel) {
          let vc = RecordViewController()
          navigationController?.pushViewController(vc, animated: true)
      }
      
  }
  
  extension BetaClipsViewController {
      func createViewModel(
          model: Clip,
          username: String,
          completion: @escaping (Bool) -&gt; Void
      ) {
          //        StorageManager.shared.profilePictureURL(for: username) { [weak self] profilePictureURL in
          //            guard let clipURL = URL(string: model.clipUrlString),
          //                  let profilePhotoUrl = profilePictureURL else {
          //                      return
          //                  }
          
          let clipData: [ClipFeedCellType] = [
              .clip(viewModel: VideoModel(username: username,
                                          videoFileURL: model.clipUrlString))
          ]
          self.viewModels.append(clipData)
          completion(true)
          //        }
      }
  }
  </code></pre>
  <p>The cell:</p>
  <pre><code>import UIKit
  import AVFoundation
  
  protocol ClipsCollectionViewCellDelegate: AnyObject {
      func didTapProfile(with model: VideoModel)
      
      func didTapShare(with model: VideoModel)
      
      func didTapNewClip(with model: VideoModel)
  }
  
  class ClipsCollectionViewCell: UICollectionViewCell {
      
      static let identifier = &quot;ClipsCollectionViewCell&quot;
      
      var playerLooper: NSObject?
      
      // Labels
      
      private let usernameLabel: UILabel = {
          let label = UILabel()
          label.textAlignment = .center
          label.textColor = UIColor.systemPink.withAlphaComponent(0.5)
          label.backgroundColor = UIColor.systemPink.withAlphaComponent(0.1)
          label.clipsToBounds = true
          label.layer.cornerRadius = 8
          return label
      }()
      
      // Buttons
      
      private let profileButton: UIButton = {
          let button = UIButton()
          button.setBackgroundImage(UIImage(systemName: &quot;person.circle&quot;), for: .normal)
          button.tintColor = .white
          button.backgroundColor = UIColor.systemBlue.withAlphaComponent(0.1)
          button.clipsToBounds = true
          button.layer.cornerRadius = 32
          button.isUserInteractionEnabled = true
          return button
      }()
      
      private let shareButton: UIButton = {
          let button = UIButton()
          button.setBackgroundImage(UIImage(systemName: &quot;square.and.arrow.down&quot;), for: .normal)
          button.tintColor = .white
          button.backgroundColor = UIColor.systemBlue.withAlphaComponent(0.1)
          button.clipsToBounds = true
          button.layer.cornerRadius = 4
          button.isUserInteractionEnabled = true
          return button
      }()
      
      private let newClipButton: UIButton = {
          let button = UIButton()
          button.setBackgroundImage(UIImage(systemName: &quot;plus&quot;), for: .normal)
          button.tintColor = .systemOrange
          button.backgroundColor = UIColor.systemOrange.withAlphaComponent(0.1)
          button.clipsToBounds = true
          button.layer.cornerRadius = 25
          button.isUserInteractionEnabled = true
          return button
      }()
      
      private let videoContainer = UIView()
      
      // Delegate
      weak var delegate: ClipsCollectionViewCellDelegate?
      
      // Subviews
      var player: AVPlayer?
      
      private var model: VideoModel?
      
      override init(frame: CGRect) {
          super.init(frame: frame)
          contentView.backgroundColor = .black
          contentView.clipsToBounds = true
          addSubviews()
      }
      
      private func addSubviews() {
          
          contentView.addSubview(videoContainer)
          
          contentView.addSubview(usernameLabel)
          
          contentView.addSubview(profileButton)
          contentView.addSubview(shareButton)
          contentView.addSubview(newClipButton)
          
          // Add actions
          profileButton.addTarget(self, action: #selector(didTapProfileButton), for: .touchUpInside)
          shareButton.addTarget(self, action: #selector(didTapShareButton), for: .touchUpInside)
          newClipButton.addTarget(self, action: #selector(didTapNewClipButton), for: .touchUpInside)
          
          videoContainer.clipsToBounds = true
          
          contentView.sendSubviewToBack(videoContainer)
      }
      
      @objc private func didTapProfileButton() {
          guard let model = model else {
              return
          }
          delegate?.didTapProfile(with: model)
      }
      
      @objc private func didTapShareButton() {
          guard let model = model else {
              return
          }
          delegate?.didTapShare(with: model)
      }
      
      @objc private func didTapNewClipButton() {
          guard let model = model else {
              return
          }
          delegate?.didTapNewClip(with: model)
      }
      
      override func layoutSubviews() {
          super.layoutSubviews()
          
          videoContainer.frame = contentView.bounds
          
          let size = contentView.frame.size.width/7
          let width = contentView.frame.size.width
          let height = contentView.frame.size.height
          
          // Labels
          usernameLabel.frame = CGRect(x: (width-(size*3))/2, y: height-880-(size/2), width: size*3, height: size)
          
          // Buttons
          profileButton.frame = CGRect(x: width-(size*7), y: height-850-size, width: size, height: size)
          shareButton.frame = CGRect(x: width-size, y: height-850-size, width: size, height: size)
          newClipButton.frame = CGRect(x: width-size-10, y: height-175-size, width: size/1.25, height: size/1.25)
      }
      
      override func prepareForReuse() {
          super.prepareForReuse()
          usernameLabel.text = nil
          player?.pause()
          player?.seek(to: CMTime.zero)
      }
      
      public func configure(with model: VideoModel) {
          self.model = model
          configureVideo()
          
          // Labels
          usernameLabel.text = &quot;@&quot; + model.username
      }
      
      private func configureVideo() {
          guard let model = model else {
              return
          }
          
          guard let url = URL(string: model.videoFileURL) else { return }
          player = AVPlayer(url: url)
          
          let playerView = AVPlayerLayer()
          playerView.player = player
          playerView.frame = contentView.bounds
          playerView.videoGravity = .resizeAspectFill
          videoContainer.layer.addSublayer(playerView)
          player?.volume = 5
          player?.play()
          player?.actionAtItemEnd = .none
          
          NotificationCenter.default.addObserver(self,
                                                 selector: #selector(playerItemDidReachEnd(notification:)),
                                                 name: .AVPlayerItemDidPlayToEndTime,
                                                 object: player?.currentItem)
      }
      
      @objc func playerItemDidReachEnd(notification: Notification) {
           if let playerItem = notification.object as? AVPlayerItem {
               playerItem.seek(to: .zero, completionHandler: nil)
           }
       }
      
      required init?(coder: NSCoder) {
          fatalError(&quot;init(coder:) has not been implemented&quot;)
      }
      
  }
  </code></pre>