UICollectionView shouldSelectItemAtIndexPath=NO do

2020-08-09 05:24发布

问题:

I have multiple items in a CollectionView, but only a few of them should be selectable. I'm handling this with the delegate method:

- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath

My problem occurs if a selectable item is selected and in the next step the selection of a not selectable item is rejected by shouldSelectItemAtIndexPath returning NO, the selected item gets deselected anyway.

I have also tried to use

- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath

But it's the same problem.

Is this the correct desired behaviour of UICollectionView? And if yes, how can i avoid the deselection of my last selected item if a not selectable item gets selected?

回答1:

I have to correct my assumption: The last selection won't get deselected!

In order to change the appearance of the cell on selection I have overridden the setSelected accessor of UICollectionViewCell. When selecting a non selectable item, the accessor setSelected of the last selected cell gets called multiple times. First with the state NO, then with the state YES and in the end with NO again. The last state NO caused my cell to set its appearance to that of an non selected cell.

I don't know the reason for this weird behavior nor could i solve it.

My workaround is to change the appearance of selected cells directly in the ViewController.

Set the selected appearance in:

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath

Remove the selected appearance:

- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath

Check if the current cell is selected and change the appearance as intended.

   - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath


回答2:

I just had this same problem. I tried a variety of solutions and the first one that worked in my case was to reload and then reselect the selected cells. This worked for me whether I reloaded the whole collection view or just the cells that were getting stuck with a non-selected appearance.

Approaches that didn't work for me:

  • Setting the cell's selected property to true, or even toggling it false then true again.
  • Selecting the item via -selectItemAtIndexPath:animated:scrollPosition:, or even deselecting it via -deselectItemAtIndexPath: and then selecting it again.
  • Reloading just the selected rows with -reloadItemsAtIndexPaths:
  • Reloading everything with -reloadData.


回答3:

I solved this by setting self.collectionView.allowsMultipleSelection = true.

And deselecting every index when I returned true to -collectionView:shouldSelectItemAtIndexPath:



回答4:

6 Years late, but maybe helpful to someone else. In cases like this, where certain cells should not be selectable in the first place, one can simply add isUserInteractionEnabled = false to the custom UICollectionViewCell's awakeFromNib() function. This can also be done in the xib file but that's less explicit and might not be obvious to future maintainers.



回答5:

You should change cell appearance in the delegate method DidSelect and DidDeselect method. If return NO in shouldSelect method, the DidSelect and DidDeselect will not be invoked, thus the appearance will stay the same, in consistence with collectionView's selected status.



回答6:

I don't know why UICollectionView is so messy like this compared to UITableViewController... A few things I found out.

The reason why - setSelected: gets called multiple times is because of the sequence methods get called. The sequence is very similar to that of UITextFieldDelegate methods.

The method - collectionView:shouldSelectItemAtIndexPath: is called before the collectionView actually selects the cell because it's actually asking "should it be selected"?

- collectionView:didSelectItemAtIndexPath: is in fact called after the collectionView selects the cell. Hence the name "did select."

Same goes for deselection.

TL;DR - Have your collectionView deselect the cell in the delegate method - collectionView:shouldSelectItemAtIndexPath: by calling - selectItemAtIndexPath:animated:scrollPosition: and all will be fine.



回答7:

Have you tried:

- (BOOL)collectionView:(UICollectionView *)collectionView shouldDeselectItemAtIndexPath:(NSIndexPath *)indexPath

I found that this worked as desired.