Chris Chris - 1 month ago 12
iOS Question

How do you highligh two cells at the same time using UICollectionView and Swift 3.0

I'm trying to make a drum pad similar to this project written in React using

UICollectionView
. I created a "Single View Application" added a UICollectionViewController, handled the
UICollectionViewDelegate
but I am struggling to figure out how to select (and highlight) two cells simultaneously. This would allow the musician to select a Kick and a Snare at the same time.

Here is my code:

import UIKit

private let reuseIdentifier = "CustomCell"

class CollectionViewController: UICollectionViewController {

let sectionInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
let numOfPads = 24
let numOfColumns = 4

func initConfig(){
collectionView?.backgroundColor = UIColor.white
//
collectionView?.indicatorStyle = UIScrollViewIndicatorStyle.white
//
//collectionView?.backgroundView = UIView(frame: (collectionView?.frame)!)
// lets iOS recover and respond to more than one finger touch at a time
collectionView?.isMultipleTouchEnabled = true
collectionView?.allowsSelection = true
collectionView?.allowsMultipleSelection = true
collectionView?.isUserInteractionEnabled = true

// This makes tapping on the UICollectionViewCell feel zippy
collectionView?.delaysContentTouches = false
// Kill any kind of scrolling
collectionView?.isScrollEnabled = false
}

func generateRandomColor() -> UIColor {
let redValue = CGFloat(arc4random() % 255) / 255.0
let blueValue = CGFloat(arc4random() % 255) / 255.0
let greenValue = CGFloat(arc4random() % 255) / 255.0

return UIColor(red: redValue, green: greenValue, blue: blueValue, alpha: 0.5)
}

override func viewDidLoad() {
super.viewDidLoad()

// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false

// Register cell classes
self.collectionView!.register(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)

// Do any additional setup after loading the view.
initConfig()
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

// MARK: UICollectionViewDataSource

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


override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 24
}

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)

cell.backgroundColor = generateRandomColor()

return cell
}


// MARK: UICollectionViewDelegate

override func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
if let selectedItems = collectionView.indexPathsForSelectedItems {
if selectedItems.contains(indexPath as IndexPath) {
collectionView.deselectItem(at: indexPath as IndexPath, animated: true)
return false
}
}
return true
}

override func collectionView(_ collectionView: UICollectionView, didHighlightItemAt indexPath: IndexPath) {
print("didHighlightItemAt", indexPath)
let cell = collectionView.cellForItem(at: indexPath)! as UICollectionViewCell
cell.contentView.backgroundColor = UIColor.white
}

override func collectionView(_ collectionView: UICollectionView, didUnhighlightItemAt indexPath: IndexPath) {
//print("didUnhighlightItemAt", indexPath)
let cell = collectionView.cellForItem(at: indexPath)! as UICollectionViewCell
cell.contentView.backgroundColor = UIColor.clear
}

override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("didSelectItemAt", selectedCells)

}
}

Answer

To "activate" multiple Cell selection, you just need to allow it, using the shouldSelectItemAt function of the UICollectionViewDelegate.

But in your case, we need to activate multiple Cell touch. To be able to do so, I've used UIGestureRecognizers now:

First we needed to set a GestureRecognizer per Cell:

extension ViewController: UICollectionViewDataSource, UICollectionViewDelegate {

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath)

        let lpgr = UITapGestureRecognizer(target: self, action: #selector(tap(gestureReconizer:)))
        lpgr.delegate = self
        cell.addGestureRecognizer(lpgr)

        return cell
    }
}

Then we need to receive the Cell that is tapped:

extension ViewController: UIGestureRecognizerDelegate {

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }

    func tap(gestureReconizer: UITapGestureRecognizer) {
        let tapLocation = gestureReconizer.location(in: self.collectionView)

        //using the tapLocation to get the indexPath
        let indexPath = self.collectionView.indexPathForItem(at: tapLocation)

        //now we can get the cell for item at indexPath
        let cell = self.collectionView.cellForItem(at: indexPath!)
        cell?.backgroundColor = generateRandomColor()

        doWhatEver(cell: cell!)
    }

    func doWhatEver(cell: UICollectionViewCell) {
        //here we do whatever like play the desired sounds of the cells.
    }

    //or we just use your random color function for fun
    func generateRandomColor() -> UIColor {
        let redValue = CGFloat(arc4random() % 255) / 255.0
        let blueValue = CGFloat(arc4random() % 255) / 255.0
        let greenValue = CGFloat(arc4random() % 255) / 255.0

        return UIColor(red: redValue, green: greenValue, blue: blueValue, alpha: 0.5)
    }
}

Now we are able to touch Cells simultaneously.