Проигрывание плеера только в одной ячейке

avplayer
uicollectionviewcell
tableviewcell

#1

Друзья, подскажите, ищу вариант решения, как проигрывать аудио/видео только в одной ячейке. Поскольку в секции может быть 10-20 различных видео/музыки, пользователь может запустить везде функцию play. Интересует кто как реализовывал подобные решения

 func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: videCellId, for: indexPath) as! InsideVideoCell
    cell.playButton.tag = indexPath.row // 05.01
    cell.playButton.addTarget(self, action: #selector(playpause), for: .touchUpInside) // 05.01
    if let videosArray = videos?[indexPath.item] {
        queueUtility.async {
            let url = URL(string: videosArray)
            let playerItem = AVPlayerItem(url: url!)
            DispatchQueue.main.async {
                self.player = AVPlayer(playerItem: playerItem)
                self.player.actionAtItemEnd = .none
                let videoPlayerLayer = AVPlayerLayer(player: self.player)
                videoPlayerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
                videoPlayerLayer.frame = cell.bounds
                cell.insertSubview(cell.videoPlayerController.view, at: 0)
                cell.contentView.addSubview(cell.playButton)
                cell.videoPlayerController.player = self.player
                cell.videoPlayerController.view.frame = videoPlayerLayer.frame
            }
        }
    } else {
        print("Нет Видео")
    }
    return cell
}

Использую sender.tag - я запускаю/останавливаю видео в каждой ячейке, но интересует метод, как сделать возможность проигрывания только в одной ячейке
Благодарю


#2

Можно сделать отдельный менеджер, который будет принимать урл или файл для воспроизведения. И соответственно если уже что-то воспроизводится , то тормозить процесс.
В итоге получится что пользователь жмакнул на ячейку - пошло видео. Жмакнул на другую ячейку - текущее видео тормознулось, в другой начало играть. Ну как-то так.


#3

Да как вариант. Благодарю, сейчас попробую


#4

Еа гараж получилось у тебя решить данную задачу, мне тоже такое нужно но только для таблицы и для проигрывания аудиофайлов. Я буду делать как пишет Тау только через синглетон. Для такой задачи проигрывания файлов только в одной ячейке необходим единственный объект аудиоплеера.


#5

Да лови, сделал так

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    guard let cell = collectionView.cellForItem(at: NSIndexPath(row: indexPath.item, section: indexPath.section) as IndexPath) as? InsideVideoCell else { return }
   
    if cell.videoPlayerController.player?.rate == 0.0 { //&& cell.videoPlayerController.player?.currentTime() != nil
        cell.videoPlayerController.player?.play()
        cell.videoPlayerController.player?.rate = 1.0
    } else {
        cell.videoPlayerController.player?.pause()
        cell.videoPlayerController.player?.rate = 0.0

    }

#6

А зачем все так усложнять
guard let cell = collectionView.cellForItem(at: NSIndexPath(row: indexPath.item, section: indexPath.section) as IndexPath) as? InsideVideoCell else { return }

Так же будет короче и проще
guard let cell = collectionView.cellForItem(at: indexPath) as? InsideVideoCell else { return }

P.S. круговорот IndexPath в природе :slight_smile:


#7

:sweat_smile: Согласен. Как всегда спасибо за Вашу помощь :raised_hands:t2:


#8

Скажите, может у Вас был опыт, а то форум весь молчит, как можно отцентровать секцию, если только 1 из двух?

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}


func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.section == 1 {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: previewCellId, for: indexPath) as! previewCollectionViewCell
    if previewImageAll.count > 0 {
        cell.images = previewImageAll
    } else {
        print("\nEmpty Images")
    }
    return cell
}

let cell = collectionView.dequeueReusableCell(withReuseIdentifier: videoCellId, for: indexPath) as! VideoCollectionViewCell
if storiesVideoAll.count > 0 {
    cell.video = previewVideoAll
} else {
    print("\nEmpty Video")
}
return cell
}


func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if #available(iOS 11.0, *) {
    return CGSize(width: view.safeAreaLayoutGuide.layoutFrame.width, height: 300)
} else {
    return CGSize(width: view.frame.width, height: 300)
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
if section == 1 {
    return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}


#9

Еа Гараж смотри какой компактный класс написал для проигрывания музыки только в одной ячейке

import Foundation
import AVFoundation

class SoundService {
static let instance = SoundService()

var player: AVAudioPlayer?
var track = ""

func playSound(_ sound: String) {
    track = sound
    guard let soundURL = Bundle.main.url(forResource: sound, withExtension: "mp3") else { return }
    player = try? AVAudioPlayer(contentsOf: soundURL)
    player?.prepareToPlay()
    player?.play()
}

func stopSound() {
    player?.currentTime = 0
    player?.stop()
}

}

вызов функций
SoundService.instance.playSound(sound)
SoundService.instance.stopSound()

обращение к плееру
SoundService.instance.player

очень компактно и хорошо разгружает код как ячейки так и контролеров.


#10

Попробуйте посмотреть в сторону offset. Возможно через него можно.


#11

Николай круто, беру на вооружение. Вы реализовали у себя проигрывания по нажатею на ячейку или у Вас запуск как нажатие uibutton так и ячейка?


#12

Ага. Понял. Ок, буду смотреть


#13

у меня ячейка а в ней кнопка - звук вызывается по нажатию кнопки.
смотри по пунктам

в классе ячейки (выше определения класса) объявил протокол

protocol PrayCellDelegate {
func didPressSoundBtn(sound: String, inCell cell: PrayCell)
}

внутри класса ячейки объявил свойство

var delegate: PrayCellDelegate?

нажатие на кнопку в классе ячейки
@IBAction func soundBtnWasPressed(_ sender: Any) {
delegate?.didPressSoundBtn(sound: prayItem.sound, inCell: self)
}

в функции cellForRowAt indexPath

указал делегата который обработает нажатие кнопки
cell.delegate = self

и реализовал метод протокола
extension RememberVC: PrayCellDelegate {
func didPressSoundBtn(sound: String, inCell cell: PrayCell) {
if SoundService.instance.player == nil {
SoundService.instance.playSound(sound)
theСellInWhichMusicPlays = cell
theСellInWhichMusicPlays.soundBtn.setImage(UIImage(named: “pause”), for: .normal)
} else {
if (SoundService.instance.player?.isPlaying)! {
theСellInWhichMusicPlays.soundBtn.setImage(UIImage(named: “play”), for: .normal)
SoundService.instance.stopSound()
if theСellInWhichMusicPlays != cell {
SoundService.instance.playSound(sound)
theСellInWhichMusicPlays = cell
theСellInWhichMusicPlays.soundBtn.setImage(UIImage(named: “pause”), for: .normal)
}
} else {
SoundService.instance.playSound(sound)
theСellInWhichMusicPlays = cell
theСellInWhichMusicPlays.soundBtn.setImage(UIImage(named: “pause”), for: .normal)
}
}
}
}

и все работает просто зашибись и далее кайфанул от проделанной работы ))))) если понравилось решение прожми лайк


#14

Осталось научится правильно прикреплять код к сообщению… =(

Я конечно малограмотный, но чую что кидать туда-сюда ячейки - плохая идея.
И по названию тоже вопрос, что за “молящаяся ячейка” ? Может PlayCell ?


#15

А как отследить и где держать информацию о ячейке в которой проигрывается звук чтобы там его отключить и изменить рисунок кнопки, если пользователь нажал совсем уже другую ячейку? Для этого и передавал ячейку через протокол.

смотри через протокол UITableViewDelegate
передается целая таблица

func numberOfSections(in tableView: UITableView) -> Int {
return 1
}

а у меня только ее маленькая часть :joy:


#16

А кто мешает держать в контроллере нажатий indexPath?
var selectedIndexPath: IndexPath?

Ну и если он выбран, то в cellAtIndex что-то делать. И при нажатии сразу иметь 2 значения: текущее и новое.

Ячейка должна быть тупой и показывать ту инфу которую в нее запустили.


#17

сразу так не могу сообразить так как мало опыта, что значит "А кто мешает держать в контроллере нажатий indexPath?
var selectedIndexPath: IndexPath?"


#18

у тебя таблица рисуется где? в контроллере. Вот там и храни все индексы на которые пользователь нажал.
метод делагата UITableViewDelegate:

func ...didSelect at indexPath: IndexPath?... () {
    self.selectedIndexPath = indexPath
// остальные действия
}

#19

так усложнится код как мне кажется плюс к тому же нужно опять же добираться до ячеек и писать дополнительный код


#20

Нет, не усложниться. Добираться до ячеек надо в контроллере, а не кидать их из ячеек туда-сюда.
Но дело житейкое. Если код пашет - пользуйтесь и пишите так. Но сомневаюсь что это лежит в разделе best practices.