1

I have a UICollectionView and I implemented collectionView(_:didSelectItemAt:) and collectionView(_:didDeselectItemAt:). When I scroll to the bottom of the collection view and select it, I want it to stay selected, so I don't call collectionView(_:didDeselectItemAt:) in collectionView(_:didSelectItemAt:). Now, the problem is that when I scroll from the selected cell at the bottom to the cell at the top, the app crashes unexpectedly found nil while unwrapping an Optional value. My guts tell me this has to do with the dequeueing of cells under the hood.

override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let cell = collectionView.cellForItem(at: indexPath) as! ServiceMenuItemCell
        cell.backgroundColor = UIColor(colorLiteralRed: 0/255.0, green: 138/255.0, blue: 217/255.0, alpha: 1.0)

        UIView.animate(withDuration: 0.1, animations: {
            cell.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
        }, completion: { finish in
            UIView.animate(withDuration: 0.05, animations: {
                cell.transform = CGAffineTransform.identity
            })
        })
    }

    override func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
        let cell = collectionView.cellForItem(at: indexPath) as! ServiceMenuItemCell
        cell.backgroundColor = UIColor(colorLiteralRed: 177/255.0, green: 179/255.0, blue: 181/255.0, alpha: 1.0)
    }

How can I solve this correctly?

jbehrens94
  • 2,356
  • 6
  • 31
  • 59
  • Show your code with crash. What you do in `collectionView(_:didSelectItemAt:)` method? – Artem Novichkov Dec 29 '16 at 15:26
  • 1
    Possible duplicate of [How can I fix crash when tap to select row after scrolling the tableview?](http://stackoverflow.com/questions/30972392/how-can-i-fix-crash-when-tap-to-select-row-after-scrolling-the-tableview). The logic of the crash is same. Also you can see [here](http://stackoverflow.com/questions/40940014/why-will-diddeselectitemat-of-uicollectionview-throw-an-error-of-unexpected-foun?noredirect=1&lq=1) – mfaani Dec 29 '16 at 15:26
  • I added code, @ArtemNovichkov. Honey, thanks for pointing me at the link, but I don't think it really is a duplicate, as the situation is different in my opinion. – jbehrens94 Dec 29 '16 at 15:29
  • 1
    the problem happens when you scroll down in the `didDeselectItemAt` and the cell becomes `nil` because it's no longer on the screen... so it's EXACTLY the same. Explain why it's not – mfaani Dec 29 '16 at 15:33
  • When I scroll down and select an item, then scroll up to select another, both stay selected with your answer. That's when I do an optional unwrap instead of a hard unwrap of the cell. – jbehrens94 Dec 29 '16 at 15:36
  • `as!` <-- this is forced unwrapping...JUST try the answers mentioned, if it still failed then show the code failing from the other solutions – mfaani Dec 29 '16 at 15:37
  • 1
    Yeah, I know. I implemented the code from the link you put here. I unwrap the cell with `if let cell = dequeue...` and it selects two cells now. – jbehrens94 Dec 29 '16 at 15:38
  • OK so you made *some* progress. however the outcome is weird. I don't know – mfaani Dec 29 '16 at 15:43
  • Do I need to manually *deselect* the previous item, or..? Because that would become quite ugly code, and I would have to safely unwrap two cells, isn't that a memory hog? – jbehrens94 Dec 29 '16 at 15:46
  • I was about to say : *I don't know what you have done, but NO you shouldn't need to manually de-select a cell, you are listening to the callbacks from the collectionView. You must have done something which doesn't make it become deselected.*. **Then** I am now reading these 2 links :[here](http://stackoverflow.com/questions/1840614/why-does-uitableview-cell-remain-highlighted) and [here](http://stackoverflow.com/questions/19379510/uitableviewcell-doesnt-get-deselected-when-swiping-back-quickly) – mfaani Dec 29 '16 at 16:19
  • and [here](http://stackoverflow.com/questions/12106032/uitableview-does-not-automatically-deselect-the-selected-row-when-the-table-re-a) Hopefully someone can help us or we'll figure it out... – mfaani Dec 29 '16 at 16:19

1 Answers1

0

You have 2 problems:

  • a safe unwrapping in didDeSelectItemAt to avoid crashing
  • For your intended purposes: undoing what you already have done in DidSelectAtIndex, in didDeSelectItemAt

Code

override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
            let cell = collectionView.cellForItem(at: indexPath) as! ServiceMenuItemCell
            cell.backgroundColor = UIColor(colorLiteralRed: 0/255.0, green: 138/255.0, blue: 217/255.0, alpha: 1.0)

    UIView.animate(withDuration: 0.1, animations: {
        cell.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
    }, completion: { finish in
        UIView.animate(withDuration: 0.05, animations: {
            cell.transform = CGAffineTransform.identity
        })
    })
}

override func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
    guard let cell = collectionView.cellForItem(at: indexPath) as! ServiceMenuItemCell else {return}
    cell.backgroundColor = UIColor(colorLiteralRed: 177/255.0, green: 179/255.0, blue: 181/255.0, alpha: 1.0)
}

in Addition about your problem of 2 cells being selected:

In your didSelectItemAt what are you doing? You ARE transforming the cell. So in your didSelect you have to undo whatever you have done. I mean your didDeSelectItemAt is working, but whatever mutation to the cell you have done must be undone. The didDeselectItemAt helps you to do it at the right time. I'm not sure how your selection works, but I think you know better, just undo it.

lemme know if it works

mfaani
  • 33,269
  • 19
  • 164
  • 293