如何以编程方式选择UICollection视图单元格并更新指针数组?

时间:2018-07-10 20:49:25

标签: pointers uicollectionview selection avplayer

我正在将indexPath与一个指向我的数据模型的指针数组进行匹配,以查看正在播放的曲目,以便可以使用didSelectItemAt委托方法控制音频的播放。即,当点击了某个单元格时,它会根据所选单元格的indexPath来检查正在播放的歌曲。 (根据这些参数,我正在didSelectItemAt委托中播放和暂停音频)。为此,我在给定indexPath的轨道的指针数组中切换了一个布尔值。

当您手动选择单元格(点击或单击)时,此选项非常适合播放和暂停音频。

我想以编程方式选择其他单元格,但是这样做似乎没有正确设置布尔值。

这里有一些代码可以帮助解释:

  let workData = TypeData.createWorkMusicArray()
  var musicDataArray: [TypeData] = []
  var keys = [TypeData]()
  var pointerArray: [TypeData : Bool]! 
  var currentTrack: Int!

    override func viewDidLoad() {
      super.viewDidLoad()

    musicDataArray = workData

    keys = [workData[0], workData[1], workData[2]]
    pointerArray = [workData[0] : false, workData[1] : false, workData[2] : false]

    musicCollectionView.allowsMultipleSelection = false

}


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

    currentTrack = indexPath.item

    if pointerArray[keys[indexPath.item]] == false {
        pointerArray[keys[indexPath.item]] = true
        print("Playing", keys[indexPath.item])
        playAudio()
        return
    }

    pointerArray[keys[indexPath.item]] = false
    print("Stop", keys[indexPath.item])
    pause()
}

还请注意,我没有对应用程序中的任何播放逻辑使用didDeselectItemAt委托方法,因为我只有一个AVPlayer,并且在选择单元格时将新项传递到其中(取消选择方法这里只是将布尔值设置为false,以便我可以更改用户界面)

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

pointerArray[keys[indexPath.item]] = false
print("Stop", keys[indexPath.item])

}

这是我用来尝试以编程方式选择单元格的功能:

func previousTrack() {
    func getCurrentTrack() {
        if currentTrack - 1 < 0 {
            currentTrack = (musicDataArray.count - 1) < 0 ? 0 : (musicDataArray.count - 1)

        } else {
            currentTrack -= 1

        }
    }

    getCurrentTrack()

    //I've tried setting the boolean here to false but it still does not work 
    pointerArray[keys[currentTrack]] = false

    self.musicCollectionView.selectItem(at: IndexPath(item: currentTrack, section: 0), animated: true, scrollPosition: .top)
    self.musicCollectionView.delegate?.collectionView!(self.musicCollectionView, didSelectItemAt: IndexPath(item: currentTrack, section: 0))
}

  @IBAction func rewindBtnTapped(_ sender: Any) {
  previousTrack()
}

调用rewindBtnTapped时,它将选择上一个单元格,但是当我决定选择/点击/单击一个单元格时,行为就不一致,即启用播放和暂停的布尔值已经混在一起。

非常感谢您的时间-谢谢

1 个答案:

答案 0 :(得分:0)

我之前发布的代码只是向您展示有关使用变量来告诉您歌曲是否正在播放的逻辑。但这不是一个好的生产代码,主要是出于说明目的(维护字典和指针数组并不容易)。因此,我将尝试以另一种方式指导您,以使您的应用程序以后变得更易于阅读,并且您可以从控制器中分离出尽可能多的播放功能->原因:想象一下,以后您想重用例如,在同一单元格中填充一个集合视图,该视图显示例如对应于专辑的歌曲。然后,您将必须重构所有代码,以使新的集合视图也能正常工作。但是,任何面向对象编程的目的在于,您实际上可以构建可根据需要重用的“对象”。

因此,首先考虑一下您的“歌曲”对​​象到底是什么。很可能不仅会成为用户在您的收藏夹视图中点按某个单元时播放的文件的路径。我想一首歌也会有图像,长度,名称,专辑等。如果继续为每首歌需要显示的每个变量创建数组,那么最终可能会得到无数的数组,需要分别维护。那不是那么容易,而且肯定更容易失败。

首先要创建一个类或一个结构,它实际上包含您歌曲的所有数据。例如(使用struct-如果需要,可以使用class):

struct Song {

    var songName : String
    var isPlaying : Bool = false

    init(songName : String) {     
        self.songName = songName     
    }
}

我只为歌曲对象使用两个变量,但是您可以添加任意多个变量。只要注意在创建“歌曲”对​​象的实例时可以初始化哪个变量即可。在该示例中,只能初始化歌曲名称,默认情况下,将要播放的Bool初始化为false,并且该变量是告诉您歌曲是否正在播放的变量。

然后,您可以设置您的Cell,在这种情况下,它将负责根据您的“歌曲”设置所有视图。为此,您可以为该单元格创建一个自定义类,在本例中,我只是将其称为“单元格”,您可以根据需要调用它。

class Cell : UICollectionViewCell {

    var song : Song?

    weak var cellDelegate : CellDelegate?

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupViews()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func setupViews() {
        self.backgroundColor = .red
    }

    func play() { 
        if !(song?.isPlaying)! {
            song?.isPlaying = true
            print("Playing", (song?.songName)!)
            return
        }
        song?.isPlaying = false
        print("Stop", (song?.songName)!)
    }

}

请注意,单元格具有play()函数和一个名为Song var song : Song?的变量。原因是我要让每个单元决定何时播放或暂停。这会将单元格与您将在集合视图中使用的播放功能分离。

现在有了“歌曲”对​​象,您可以在视图控制器或任何其他类中轻松创建歌曲数组。例如:

var songArray : [Song] = [Song.init(songName: "Song One"), Song.init(songName: "Song Two"), Song.init(songName: "Song Three")]

最后,您可以使用歌曲数组中的每首歌曲来初始化集合视图的每个单元。为此,您将使用cellForItemAt函数:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! Cell
        cell.song = songArray[indexPath.item]
        cell.cellDelegate = self
        return cell
    }

现在,您将看到该单元格还具有一个名为var cellDelegate : CellDelegate的弱变量,这是用于控制单元格播放和暂停功能的协议。稍后您可以进一步阅读有关委托的内容,以及它们如何帮助您尽可能地将单元与控制器分离。另外,另一个集合视图控制器可能符合此委托,您将具有对单元功能的相同访问权限,而不必重写所有代码。

可以在您的单元格类之外设置协议:

protocol CellDelegate : class {
    func didSelectCell (for cell: Cell)
}

最后,使视图控制器符合CellDelegate:

class ViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource, CellDelegate

现在,播放,暂停,上一个,下一个等的代码变得更简单,更简洁

 func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        currentIndexPathItem = indexPath.item
        let cellToPlay = collectionView.cellForItem(at: IndexPath(item: currentIndexPathItem, section: 0)) as! Cell
        didSelectCell(for: cellToPlay)
    }

    func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
        let cellToStop = collectionView.cellForItem(at: indexPath) as! Cell
        if (cellToStop.song?.isPlaying)! { didSelectCell(for: cellToStop) }
    }

    @objc func previous () {
        let playingCell = collectionView.cellForItem(at: IndexPath(item: currentIndexPathItem, section: 0)) as! Cell
        if (playingCell.song?.isPlaying)! { didSelectCell(for: playingCell) }
        if currentIndexPathItem - 1 < 0 { return }
        else { currentIndexPathItem = currentIndexPathItem - 1 }
        let cellToPlay = collectionView.cellForItem(at: IndexPath(item: currentIndexPathItem, section: 0)) as! Cell
        didSelectCell(for: cellToPlay)
    }

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

请注意didSelectCell函数,这是添加所有内容以符合委托人的要求,并播放或暂停您的歌曲。就是这样。更干净,更简单的代码。