50583

UICollectionView deselectItem when cell pre-fetching is enabled

On iOS 10.0, UICollectionView pre-fetches cells by default. This leads to cells that are prepared for being shown on screen, but are hidden. This question describes it really well.

The following code will successfully deselect an index path when its cell is either visible or does not exist at all. If the cell exists and is hidden, the index path will be deselected, but the cell becomes stuck in the selected state until it is reused.

collectionView!.deselectItem(at: indexPath, animated: false)

This problem does not exits on iOS 9 or when pre-fetching is disabled with isPrefetchingEnabled = false on iOS 10.0.

Is this a bug in UICollectionView or am I misunderstanding how deselectItem is supposed to work?

Here is the full code of a UICollectionViewController subclass that demonstrates this behaviour with the following steps:

    <li>Tap on a cell, so that it becomes selected (red)</li> <li>Scroll the cell slightly off-screen</li> <li>Tap the "Deselect Cell" button</li> <li>Scroll the cell back on screen</li> <li>Observe how it still looks selected</li> <li>Tap on another cell</li> <li>Observe how both cells look selected</li> <li>Scroll the first cell far off-screen and back again</li> <li>Observe how the first cell finally does not look selected</li> </ul>

    import UIKit private let reuseIdentifier = "Cell" class CollectionViewController: UICollectionViewController { override func viewDidLoad() { super.viewDidLoad() self.collectionView!.register(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier) let button = UIButton(frame: CGRect(x: 10, y: 30, width: 360, height: 44)) button.backgroundColor = #colorLiteral(red: 0.9686274529, green: 0.78039217, blue: 0.3450980484, alpha: 1) button.setTitleColor(#colorLiteral(red: 1, green: 1, blue: 1, alpha: 1), for: .normal) button.setTitleColor(#colorLiteral(red: 0.05882352963, green: 0.180392161, blue: 0.2470588237, alpha: 1), for: .highlighted) button.setTitle("Deselect Cell", for: .normal) button.addTarget(self, action: #selector(CollectionViewController.buttonPress), for: .touchUpInside) view.addSubview(button) } func buttonPress() { for indexPath in collectionView!.indexPathsForSelectedItems ?? [IndexPath]() { let cell = collectionView!.cellForItem(at: indexPath) NSLog("Deselecting indexPath: %@, cell: %@", indexPath.description, cell?.frame.debugDescription ?? "not visible") collectionView!.deselectItem(at: indexPath, animated: false) } } override func numberOfSections(in collectionView: UICollectionView) -> Int { return 1 } override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 300 } override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) cell.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1) cell.selectedBackgroundView = UIView(frame: cell.bounds) cell.selectedBackgroundView!.backgroundColor = #colorLiteral(red: 0.9254902005, green: 0.2352941185, blue: 0.1019607857, alpha: 1) return cell } }

    Answer1:

    As far as I can tell this is a bug in UICollectionView, and I've opened a Radar myself. You might want to do so as well to apply more pressure.

    Even before prefetching, collection view didn't bother deselecting cells that weren't visible. Even cellForItemAtIndexPath: states that it returns nil for cells that are not visible.

    But before prefetching, as soon as a cell left the content view it was added to the reuse pool, and when you scrolled back you got a reused cell that had its selected state reset.

    Now, that cell remains loaded and wont reset until it's reused, by scrolling further away, beyond the prefetching area.

    To fix this you can either set prefetchingEnabled to NO and lose its benefits, or update selection state whenever a cell appears -

    - (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath { if (!cell.isSelected) { return; } if ([[collectionView indexPathsForSelectedItems] containsObject:indexPath]) { return; } cell.selected = NO; }

    This doesn't seem to diminish performance.

Recommend

  • UICollectionViewCell Selecting multiple cells swift
  • Override parrents onClick event with checkbox onClick?
  • Get all text between tags with preg_match_all() or better function?
  • innerHTML not being updated after changing value of input (checked = true/false/“checked”)
  • Problem modifying UITableViewCell accessoryType in tableView:didSelectRowAtIndexPath:
  • npm browserify version of jquery-select2 version 4.x
  • SSIS Script Component - Changing target .net version keeps reverting
  • Using an R Markdown Document as a source for functions
  • How to print .MSG into PDF
  • Sometimes strange artifact appears when [mapView selectAnnotation];
  • Using a RST Document in a ScrollView using Kivy
  • Open Fragment From Activity
  • CodeEditor example in pyqt
  • Correct way to start RSpec-puppet unit tests
  • Can't fail SVN pre-commit script with Windows server
  • Compile errors on deployed web application, but not in the IDE
  • Blackberry: Read a text file packaged in the project (faster)
  • File uploading and saving to database incorrectly
  • Use WPF object to 'punch' hole in another?
  • Why are Pickle files in Pickle protocol 4 twice as large as those in protocol 3 without having any g
  • Fastest way to decode a hexadecimal digit
  • Python how to create a dict of dict of list with defaultdict
  • and events (INotifyPropertyChanged, specifically)
  • set body width in px?
  • How can you Call a method from a diffrent Project, both in C++?
  • How to create list with carousel effect in android
  • @ngboostrap Bootstrap's JavaScript requires jQuery. jQuery must be included before Bootstrap�
  • WebSphere MQ 6 message segmentation option ignored in put()?
  • Injecting service into a mixin Ember2.3+
  • How to reshape a 3D numpy array?
  • How to select table rows/complete table?
  • Is there a way to call library thread-local init/cleanup on thread creation/destruction?
  • Classic ASP URL Rewriting
  • Efficient User-Agent Regex to find Safari in Python
  • Julia: How to give multiple workers access to functions that are 'include(…)' into a modul
  • Center align outputs in ipython notebook
  • custom UITableViewCell with image for highlighting
  • Java applet as stand-alone Windows application?
  • Sending data from AppleScript to FileMaker records
  • vba code to select only visible cells in specific column except heading