1

I am trying to show the selected cell in my UICollectionView by changing its border and shadow color. my issue is that my DidDeselect function causes a crash if I select a cell then scroll the selected cell of screen and select another cell, I understand that is because my DidDeselect is trying to deselect a cell that has been reused so my question is:

How do I remove the border from a cell that is no longer visible once it has scrolled off the screen

My DidSelect:

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    let cell = collectionView.cellForItem(at: indexPath as IndexPath) as! PresentationCell
    cell.layer.borderColor = #colorLiteral(red: 0.9607843161, green: 0.7058823705, blue: 0.200000003, alpha: 1)
    cell.layer.borderWidth = 3
    cell.layer.shadowColor = #colorLiteral(red: 0.9607843161, green: 0.7058823705, blue: 0.200000003, alpha: 1)
}

My DidDeSelect:

func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
    let cellItem = collectionView.cellForItem(at: indexPath) as! PresentationCell
    cellItem.layer.borderColor = UIColor.clear.cgColor
    cellItem.layer.borderWidth = 3
    cellItem.layer.shadowColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)
 }

It crashes on this line:

let cellItem = collectionView.cellForItem(at: indexPath) as! PresentationCell

Error is : Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value

Which I understand is because the cell I selected first is no longer displayed because I've scrolled it out of view and then selected a cell at the bottom of the collectionView

How do I get around this?

elevate
  • 147
  • 7

2 Answers2

0

Elevate you not doing proper way to achieve your requirements

below i suggest to way achieving your requirement.

put your border colour and width in your cell method

  func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
       let cell = collectionView.dequeueReusableCell(withReuseIdentifier: 
      "id_PresentationCell", for: indexPath) as! PresentationCell
   if arrYourData[IndexPath.row].isSelected == true {
        cell.layer.borderColor = #colorLiteral(red: 0.9607843161, green: 0.7058823705, blue: 0.200000003, alpha: 1)
        cell.layer.borderWidth = 3
        cell.layer.shadowColor = #colorLiteral(red: 0.9607843161, green: 0.7058823705, blue: 0.200000003, alpha: 1)
  }
        
}
  

When tap on didSelected Make isSelected true on tap like beolow :

    func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
        self.arrYourData[IndexPath.row].isSelected = !self.arrYourData[IndexPath.row].isSelected 

**Reload your collection here**
     }
nimesh surani
  • 177
  • 1
  • 13
0

It is a correct behavior. When you use the dequeueReusableCell cell are reused and memory gets allocated to visible cells only. So, if you try to pull a cell which is already deallocated then it will gives you nil.

In your case, you need to have some custom implementation where you can hold the selection and apply the logic.

Following is the working code...

Declare the variable at top

var mySelection = -1

Use this code block for rest of logic

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
 {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
    cell.backgroundColor = .blue
    if mySelection == indexPath.row {
      applySelection(cell: cell, index: indexPath.row)
    } else {
      removeSelection(cell: cell, index: indexPath.row)
    }
    return cell
 }

override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) 
 {
    mySelection = indexPath.row
    if let cell = collectionView.cellForItem(at: indexPath as IndexPath) 
    {
      applySelection(cell: cell, index: indexPath.row)
    }
 }

override func collectionView(_ collectionView: UICollectionView, 
 didDeselectItemAt indexPath: IndexPath) 
 {
    if let cellItem = collectionView.cellForItem(at: indexPath) 
    {
      removeSelection(cell: cellItem, index: indexPath.row)
    }
 }

func applySelection(cell: UICollectionViewCell, index: Int) {
      cell.layer.borderColor = #colorLiteral(red: 0.9607843161, green: 0.7058823705, blue: 0.200000003, alpha: 1)
      cell.layer.borderWidth = 3
      cell.layer.shadowColor = #colorLiteral(red: 0.9607843161, green: 0.7058823705, blue: 0.200000003, alpha: 1)
 }

func removeSelection(cell: UICollectionViewCell, index: Int) {
    cell.layer.borderColor = UIColor.clear.cgColor
    cell.layer.borderWidth = 3
    cell.layer.shadowColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)
 }
RJ168
  • 1,006
  • 2
  • 12
  • 22
  • If you want to have multiple selections, then use array to hold selection instate of `mySelection` variable. It will change your comparison condition in `cellForItemAt` method. – RJ168 Mar 26 '21 at 05:16