Как определить, использую ли я один и тот же элемент для заполнения нескольких UICollectionViewCell? - PullRequest
0 голосов
/ 18 сентября 2018

Я занимаюсь разработкой музыкального приложения, в котором пользователи могут просматривать выбранные дорожки и сохранять их в списке воспроизведения.У меня возникла проблема, когда при сохранении дорожки в список воспроизведения дважды, один раз за другим, элементы управления воспроизведением не работают должным образом, т. Е. Добавляется 2x дорожка1 в начале списка воспроизведения - при выборе 1-й дорожки1 - воспроизведение и пауза простохорошо, тогда, если я выберу 2-ю дорожку 1 (если 1-я дорожка 1 уже выбрана, или наоборот), она продолжит воспроизведение / паузу, как если бы это была 1-я дорожка 1.

Есть ли способ дублировать этот элемент вместо ссылки на тот же элемент?Вот некоторый код, который показывает, как я заполняю списки воспроизведения, мою структуру данных и т. Д.

Структура данных:

class Data: NSObject, NSCoding {

   var title: String
   var body: String
   var colour: UIColor
   var url: String
   var isPlaying : Bool = false

  init(title: String, body: String, colour: UIColor, url: String) {
    self.title = title
    self.body = body
    self.colour = colour
    self.url = url
}

class func createTrackArray() -> [Data] {
    var array: [Data] = []

    let track1 = Data(title: "Track 1 Title", body: "Track 1 Body", colour: .white, url: "track1url")
    let track2 = Data(title: "Track 2 Title", body: "Track 2 Body", colour: .white, url: "track2url")

    array.append(track1)

    return array
   }

 }

Отображение музыки для просмотра:

//Global Variable
var musicDataArray: [Data] = []

class MusicVC: UIViewController {

   override func viewDidLoad() {
    super.viewDidLoad()

     musicDataArray = Data.createTrackArray()
  }

}

//MARK: - CollectionView Cell Configuration

extension MusicVC: UICollectionViewDelegate, UICollectionViewDataSource, CellDelegate {

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

func numberOfSections(in collectionView: UICollectionView) -> Int {
    return 1
}

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

    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: .musicCell, for: indexPath) as! MusicCell

    cell.cellData = musicDataArray[indexPath.item]
    cell.song = musicDataArray[indexPath.item]

    cell.cellDelegate = self
    return cell
}

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

    currentIndexPathItem = indexPath.item

    guard let cellToPlay = musicCollectionView.cellForItem(at: IndexPath(item: currentIndexPathItem, section: 0)) as? MusicCell else {
        print("Cell not here")
        return
    }
    didSelectCell(for: cellToPlay)

    prepareTrackForSavingToPlaylist(track: indexPath.item)
}

   func didSelectCell(for cell: MusicCell) {
     cell.play()
}

Эта функция сохраняет индекс (и связанный элемент массива ??) во временном массиве.Затем я отправляю его через segue другому контроллеру представления, в результате чего он сохраняется в списке воспроизведения.то есть playlistArray будет содержать только один элемент, который он передает следующему контроллеру представления.

func prepareTrackForSavingToPlaylist(track: Int) {
    playlistArray.removeAll()
    playlistArray.append(musicDataArray[track])
}

Next ViewController: затем добавляются пропущенные данные (которые являются информацией из этого временного массива из предыдущего контроллера представления)к любому списку воспроизведения, который они выбирают в виде коллекции

 func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

 let passedData = Data(title: dataFromMusicVC.title, body: dataFromMusicVC.body, colour: dataFromMusicVC.colour, url: dataFromMusicVC.url)
        playlistArray[indexPath.item].append(passedData)
}

И, наконец, пользователь может выбрать список воспроизведения, и он отобразит все сохраненные дорожки в этом списке воспроизведения

extension UserPlaylistsVC: UICollectionViewDelegate, UICollectionViewDataSource, UserDelegate {

func didSelectCell(for cell: UserPlaylistsCell) {
    cell.play()
}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return  playlistArray[playlistIndex].count
}

func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
    return 0
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let createID = "UserCell"
    let userCell = collectionView.dequeueReusableCell(withReuseIdentifier: createID, for: indexPath) as! UserPlaylistsCell

 //playlistIndex is just an Int that is based on which cell is selected in a different view controller so that the right playlist is accessed.
    userCell.song = playlistArray[playlistIndex][indexPath.item]
    userCell.cellData = playlistArray[playlistIndex][indexPath.item]

    userCell.userDelegate = self
    userCell.delegate = self

    return userCell
}

Этоячейка, которая используется для дорожек в списках воспроизведения:

    protocol UserDelegate: class {
func didSelectCell (for cell: UserPlaylistsCell)
 }

 class UserPlaylistsCell: UICollectionViewCell {

@IBOutlet weak var titleLbl: UILabel!
@IBOutlet weak var bodyLbl: UILabel!
@IBOutlet weak var colourView: UIView!

var song : TypeData!
var audio = Audio()

weak var delegate: UserPlaylistDelegate?
weak var userDelegate: UserDelegate?

override func prepareForReuse() {
    super.prepareForReuse()
}

override var isSelected: Bool {
    didSet {
        self.contentView.backgroundColor = isSelected ? UIColor.UMLightGrey : UIColor.white
    }
}

override func awakeFromNib() {
    super.awakeFromNib()

    titleLbl.textColor = UIColor.UMDarkGrey
    bodyLbl.textColor = UIColor.UMDarkGrey
    colourView.layer.cornerRadius = colourView.layer.frame.size.width / 2
    colourView.clipsToBounds = true
}

var cellData: TypeData! {
    didSet {
        titleLbl.text = cellData.title
        bodyLbl.text = cellData.body
        colourView.backgroundColor = cellData.colour
    }
}

func play() {

    if !(song?.isPlaying)! {
        song?.isPlaying = true

     //Checking whether the global variable is the same as the selected song url so that I don't have to fetch the asset again (fetching the asset plays the track from the beginning again)

        if urlString == song.url {
            player.play()
        } else {
            urlString = song.url
            audio.fetchAsset()
            audio.playAsset()
        }
        return
    }

    song?.isPlaying = false
    player.pause()

    print("Stop", (song?.title)!)
}

}

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

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

РЕДАКТИРОВАТЬ

Вот метод didSelectItemAt согласно запросу:

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

    currentItemIndex = indexPath.item

    guard let cellToPlay = userPlaylistsCollectionView.cellForItem(at: IndexPath(item: currentItemIndex, section: 0)) as? UserPlaylistsCell else {
        return
    }

    didSelectCell(for: cellToPlay)
}

РЕДАКТИРОВАТЬ

didDeselectMethod:

    func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {

    if let cellToStop = userPlaylistsCollectionView.dataSource?.collectionView(userPlaylistsCollectionView, cellForItemAt: indexPath) as? UserPlaylistsCell {
        if (cellToStop.song.isPlaying) {
            didSelectCell(for: cellToStop)
            print(cellToStop.song.isPlaying)
        }
    }
}

Ответы [ 3 ]

0 голосов
/ 18 сентября 2018

Вы используете class для объектов вашей песни. Это означает, что они хранятся в памяти по ссылке, поэтому, когда вы добавляете одну и ту же песню в массив дважды, доступ к первому или второму экземпляру изменит один и тот же объект в памяти. В вашем случае я бы переключился на использование struct для ваших «данных». struct - тип значения, и экземпляры копируются вместо ссылок. Подробнее об этом в Swift docs

Также очень важно не переопределять имена системных классов, поскольку Data - это стандартный псевдоним типа Swift, который вы, скорее всего, будете использовать вне логики моделей песен.

0 голосов
/ 22 сентября 2018

Не думаю, что это проблема с несколькими ссылками на один и тот же элемент.Кажется, что все ваши данные в массиве не обновляются соответственно, когда обновляются отдельные данные в массиве.Может быть, я не смог найти логику в ваших кодах.Не обращайте внимания, если это так.

Допустим, у вас есть два трека, добавленных в список воспроизведения, и один воспроизводится.Затем, когда вы проигрываете другой трек, первое значение isPaying должно быть обновлено до «false», пока включен второй.Вы должны убедиться, что это происходит.

Несколько рекомендаций.

  1. Удаляет все логики рядом с логикой для отображения данных из ячейки, таких как большая часть кода, имеющегося в вашей игре () функция.Переместите обновление данных туда, где вы можете получить доступ ко всем данным.(Возможно, посмотрите на контроллер для вашего случая) Затем обновите не только состояние этих отдельных данных, но и весь набор данных, чтобы синхронизировать все данные.

или

Удалите 'isPlaying' из класса Data и разрешите другому менеджеру или контроллеру отслеживать текущую воспроизводимую песню, чтобы вам не приходилось беспокоиться об одной или не воспроизводящейся песне всегда.

Попытка передать значимые данные каждому контроллеру представления.Например, кажется, что ваш контроллер последнего просмотра не должен знать о другом списке воспроизведения.Поэтому вместо того, чтобы получать песню из playlistArray [playlistIndex] [indexPath.item], лучше иметь такую ​​структуру данных, как playlist [indexPath.item].

Убедитесь, что ваша реализация выполненаПраво, когда вы делаете сравнение между объектом.Похоже, вы можете добавить две одинаковые песни в один и тот же список воспроизведения, и сравнение song.url в функции play () не выглядит многообещающим.Даже сравнительное сравнение звучит опасно для меня.Что ты должен делать?Трудно сказать в данном контексте.Опять же, может быть лучше, если какой-либо другой источник сообщит ячейке, играет ли она в данный момент или нет, и ячейка отображает только свой статус.

В целом ваши требования не выглядят сложными.

0 голосов
/ 18 сентября 2018

Проблема может быть здесь

if urlString == song.url {
        player.play()
    } else {
        urlString = song.url
        audio.fetchAsset()
        audio.playAsset()
    }

Это основано на том факте, что две песни имеют разные URL, но если дорожка одинакова, они, несомненно, имеют одинаковые URL. Попробуйте использовать другую систему кеширования, например, массив, содержащий идентификатор предыдущей / воспроизводимой в данный момент песни / песни (например, на основе indexPath ячейки).

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

...