繁体   English   中英

如何判断我是否使用同一项目来填充多个UICollectionViewCell?

[英]How to tell if I am using the same item to populate more than one UICollectionViewCell?

我正在开发一个音乐应用程序,用户可以通过它浏览选定的曲目并将其保存到播放列表中。 我遇到一个问题,如果将曲目两次保存到一个播放列表中,一次又一次,则播放控件将无法正常运行,即在播放列表的开头添加了2x track1-如果选择第一个track1,则会播放并暂停很好,那么如果我选择第二音轨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])
}

下一个ViewController:传递的数据(这是前一个视图控制器从该临时数组获得的信息)随后被添加到他们在collectionview中选择的任何播放列表

 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)!)
}

}

您是否知道为什么将同一首曲目连续两次保存到播放列表中并选择播放?如果控件是另一首曲目,则控件的行为就不一样,即所需功能是将其视为完全不同的曲目,选择该曲目会从头开始播放,同时还保留暂停和单独播放的能力。

如果您需要更多说明或其他代码,请随时询问。 非常感谢您抽出宝贵的时间解决此问题。

编辑

这是所请求的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)
        }
    }
}

问题可能在这里

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

这是基于以下事实:两首歌曲具有不同的url,但是,如果曲目相同,则它们肯定具有相同的url。 尝试使用其他缓存系统,例如包含先前/当前播放/正在播放的歌曲ID(例如,基于单元indexPath的Array)的Array。

无论如何,让用户在播放列表中多次拥有相同的曲目是一种不良行为。

您正在为您的歌曲对象使用class 这意味着它们通过引用存储在内存中,因此当您将同一首歌曲两次添加到数组中时,访问第一个或第二个实例将修改内存中的同一实体 在您的情况下,我将改用struct作为“数据”。 struct是值类型,实例被复制而不是被引用。 Swift文档中的更多内容

同样重要的是不要覆盖系统类名称,因为Data是标准的Swift类型别名,您很可能会在歌曲模型的逻辑之外使用它。

我不认为这是一个重复引用同一项目的问题。 似乎更新数组中的单个数据时,数组中的整个数据没有相应地更新。 也许我在您的代码中找不到逻辑。 如果是这样,请忽略。

假设您有两首曲目已添加到播放列表中,并且其中一首已播放。 然后,当您播放另一首曲目时,在打开第二首曲目时,第一个的isPaying值应更新为“ false”。 您应该确定是否正在发生。

很少的建议。

  1. 删除逻辑旁边的所有逻辑以显示单元格中的数据,例如play()函数中包含的大多数代码。 将数据更新移动到可以访问整个数据的位置。 (可能需要查看您的案例的控制器)然后不仅更新该单个数据的状态,而且还更新整个数据集,以便您将所有数据进行同步。

要么

  1. 从Data类中删除“ isPlaying”,然后让其他经理或控制器跟踪当前正在播放的歌曲,因此您无需担心总是播放一首或一首歌曲。

  2. 试图将有意义的数据传递给每个视图控制器。 例如,您的“最后一个”视图控制器似乎不需要了解其他播放列表。 因此,与其从playlistArray [playlistIndex] [indexPath.item]中获取一首歌曲,不如让它具有像playlist [indexPath.item]这样的数据结构会更好。

  3. 在对象之间进行比较时,请确保您的实现正确。 听起来您可以将两首相同的歌曲添加到同一播放列表中,并且在play()函数中比较song.url似乎没有希望。 甚至参考比较对我来说也很危险。 你该怎么办? 在给定的背景下很难分辨。 同样,如果其他一些来源让单元知道当前是否正在播放,并且该单元仅显示其状态,则可能会更好。

总体而言,您的要求看起来并不难。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM