Как я могу настроить представление коллекции AVPlayers для воспроизведения видео только в текущей выбранной ячейке?

#ios #uicollectionview #uicollectionviewcell #avplayer #swift5

Вопрос:

У меня есть настройка UICollectionView, в которой есть ячейки видеозаписей из моей базы данных. Прямо сейчас, когда загружено представление коллекции, начинают воспроизводиться все видео в разных ячейках. Я хочу, чтобы видео не воспроизводилось ни в каких ячейках, кроме выбранной ячейки, чтобы видео-аудиозаписи не воспроизводились друг над другом. Как я могу это сделать? Вот код…

Контроллер представления:

 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 = ""    // for _ in 0..lt;10 {  // let model = VideoModel(username: "@CJMJM",  // videoFileURL: "https://firebasestorage.googleapis.com:443/v0/b/globe-e8b7f.appspot.com/o/clipvideos/1637024382.mp4?alt=mediaamp;token=c12d0481-f834-4a17-8eee-30595bdf0e8b")  // 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: "username") 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("failed to create VM")  }  }  )  }    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("profile tapped")  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("tapped share")  }    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)  // }  } }  

В камере:

 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 = "ClipsCollectionViewCell"    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: "person.circle"), 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: "square.and.arrow.down"), 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: "plus"), 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 = "@"   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("init(coder:) has not been implemented")  }   }