如何以编程方式 select UICollection 查看单元格并更新指针数组?

How can I programmatically select UICollection view cells and update a pointer array?

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

这非常适合在您手动 select 单元格(点击或单击)时播放和暂停音频。

我想以编程方式 select 其他单元格,但是当我这样做时,似乎布尔值设置不正确。

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

  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,并且当单元格为 selected(此处的 deselect 方法只是将布尔值设置为 false,因此我可以更改 UI)

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

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

}

这是我用来尝试以编程方式 select 单元格的函数:

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 时,它将 select 前一个单元格,但是当我决定 select/tap/click 一个单元格时,行为不一致,即启用播放和暂停的布尔值已经搞混了。

非常感谢您的宝贵时间 - 谢谢

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

所以,首先想一想您的 "song" object 究竟是什么。很可能不仅是一个文件的路径,只要用户从您的 collection 视图中点击一个单元格,该文件就会被播放。我想一首歌也会有图像、长度、名称、专辑等。如果您继续为歌曲需要显示的每个变量创建数组,您最终可能会得到无数需要单独维护的数组.这并不容易,而且肯定更容易失败。

第一件事是创建一个 class 或一个实际包含歌曲所有数据的结构。例如(使用结构 - 如果需要,可以使用 class):

struct Song {

    var songName : String
    var isPlaying : Bool = false

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

我只为歌曲 object 使用了两个变量,但您可以根据需要添加任意多个变量。只需注意在创建"Song" object 实例时可以初始化哪个变量即可。示例中只能初始化歌曲名,播放的Bool默认初始化为false,是判断歌曲是否播放的变量。

然后您可以设置您的 Cell,在这种情况下,它将根据您的 "Song" 负责设置所有视图。为此,您可以为单元格创建一个自定义 class,在这种情况下,我只是将其命名为 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? 的变量。原因是我要让每个单元格决定何时播放或暂停。这会将单元格与您将在 collection 视图中使用的播放功能分离。

现在您有了 "Song" object,您可以轻松地在视图控制器或任何其他 class 中创建歌曲数组。例如:

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

最后,您可以使用歌曲数组中的每首歌曲初始化 collection 视图的每个单元格。为此,您将使用 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 的弱变量,这是您将用来控制单元格播放和暂停功能的协议。您可以稍后进一步阅读有关委托的内容,以及它们如何帮助您尽可能地将您的单元与控制器分离。此外,另一个 collection 视图控制器可能符合此委托,您将拥有对单元格功能的相同访问权限,而无需重写所有代码。

协议可以在您的手机外设置 class:

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 函数,这是您需要添加的所有内容以符合委托并播放或暂停您的歌曲。就是这样。代码更简洁、更简单。