如何以编程方式选择UICollection视图单元格并更新指针数组? [英] How can I programmatically select UICollection view cells and update a pointer array?

查看:79
本文介绍了如何以编程方式选择UICollection视图单元格并更新指针数组?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm matching the indexPath with a pointer array against my data model to see which track is playing so that I can control audio playback using the didSelectItemAt delegate method. i.e. when a cell has been tapped it checks to see which song is playing based off the indexPath of the selected cell. (I am playing and pausing the audio within the didSelectItemAt delegate based off those parameters). I do this by switching a boolean in the pointer array for the track at the given indexPath.

当您手动选择单元格(轻按或单击)时,这非常适合播放和暂停音频.

This works great for playing and pausing the audio when you are manually selecting the cells (tapping or clicking).

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

I would like to select other cells programmatically however when I do so it seems the boolean is not set correctly.

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

Here is some code to help explain:

  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,以便我可以更改用户界面)

Also note that I do not use didDeselectItemAt delegate method for any of playback logic in my app as I only have one AVPlayer and a new item is passed into when the cell is selected (The deselect method here just sets the boolean to false so I can change the UI)

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

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

}

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

This is the function I am using to try and programatically select the cells:

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时,它将选择上一个单元格,但是当我决定选择/点击/单击一个单元格时,行为就不一致,即启用播放和暂停的布尔值已经混在一起.

When rewindBtnTapped is called it will select the previous cell but then when I decide to select/tap/click on a cell the behaviour isn't consistent i.e. the booleans that enable the playing and pausing have been mixed up.

我真的很感谢您的时间-谢谢

I really appreciate your time - Thank you

推荐答案

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

The code I posted before was just to show you a little bit about the logic of having a variable that tells you if the song is playing or not. But it is not a good production code, it was mostly for illustration purposes (it is not easy to maintain a dictionary and a pointer array). So, I'm gonna try to guide you in a different way so that your app becomes simpler to read later and you detach the function of playing as much as possible from your controller -> Reason: Imagine that later you would like to reuse the same cell to populate a collection view that displays the songs that correspond to an Album for example. Then you will have to refactor all the code to make your new collection view work as well. However, the point of any object oriented programming is that you can actually build "Objects" that can be reused as you may need.

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

So, first think what exactly your "song" object will have to be. Most probably is not only gonna be a path to a file that gets played anytime a user taps a cell from your collection view. I guess a song will also have an image, length, name, album, etc etc. If you continue creating arrays for each single variable that a song will need to display, you will probably end up with countless arrays that need to be maintained separately. That is not so easy and is surely more prone to fails.

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

The first thing the will be to create a class or an struct that actually contains all the data of you song. For example (using struct - you can use class if you want):

struct Song {

    var songName : String
    var isPlaying : Bool = false

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

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

I'm only using two variables for the song object but you can add as many as you want. Just pay attention to which variable can be initialized when you create an instance of your "Song" object. In the example only the song name can be initialized, the Bool for playing is initialized by default to false and is the variable that tells you if the song is playing or not.

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

Then you can setup you Cell, which in this case will be the responsible for setting all the views depending on your "Song". For this you can create a custom class for the cell, in this case I just called it Cell, you can call it whatever you want.

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?的变量.原因是我要让每个单元决定何时播放或暂停.这会将单元格从您将在集合视图中使用的播放功能中分离出来.

Notice that cell has play() function and a variable called song var song : Song?. The reason is that I am going to let each cell decide when to play or pause. This detaches the cell from the play function that you would use in collection view.

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

Now that you have a "Song" object you can easily create an array of songs in your view controller or any other class. For example:

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

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

Finally you can initialize each of the cells of the collection view with each of the songs in your song array. For that you will use the cellForItemAt function:

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的弱变量,这是用于控制单元格播放和暂停功能的协议.稍后您可以进一步阅读有关委托的内容,以及它们如何帮助您尽可能地将您的单元与控制器分离.另外,另一个集合视图控制器可能符合此委托,您将具有对单元功能的相同访问权限,而不必重写所有代码.

Now, you will see that the cell also has a weak var called var cellDelegate : CellDelegate this is the protocol that you will use to control the cell play and pause function. You can read further about delegates later and how they help to detach your cell from the controller as much as possible. Also, another collection view controller may conform to this delegate and you will have the same access to the function of the cell without having to rewrite all the code.

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

The protocol can be set outside of your cell class:

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

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

Finally, conform the view controller to CellDelegate:

class ViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource, CellDelegate

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

And now the code for playing, pausing, previous, next, etc etc becomes way simpler and cleaner

 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功能,这是添加所有内容以符合委托人的要求,并播放或暂停您的歌曲.就是这样.更干净,更简单的代码.

Notice the didSelectCell function, that is all you need to add to conform to the delegate and play or pause your song. And that is it. Much cleaner and much simpler to code.

这篇关于如何以编程方式选择UICollection视图单元格并更新指针数组?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆