Deselect UICollectionView cell on second tap

Try using the "shouldSelectItem" UIColllectionViewDelegate method.

func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
    let item = collectionView.cellForItem(at: indexPath)
    if item?.isSelected ?? false {
        collectionView.deselectItem(at: indexPath, animated: true)
    } else {
        collectionView.selectItem(at: indexPath, animated: true, scrollPosition: [])
        return true
    }

    return false
}

A shorter version of @pkorosec answer, with exact the same effect, is the following:

override func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
    if collectionView.cellForItem(at: indexPath)?.isSelected ?? false {
        collectionView.deselectItem(at: indexPath, animated: true)
        return false
    }
    return true
}

An alternative, suggested by @Manav, is:

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

Another option, that I personally think is way cleaner, is to allow multiple selection on collection view and then manually deselect the currently selected item before next selection.

First step: allow multiple selection

override func viewDidLoad() {
    super.viewDidLoad()
    collectionView.allowsMultipleSelection = true
}

Second step: manually deselect previously selected item

func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
    collectionView.indexPathsForSelectedItems?.forEach { ip in
        collectionView.deselectItem(at: ip, animated: true)
    }
    return true
}