Uicollectionview Select and Deselect Issue

UICollectionView issue with Select/Deselect cell

As I understand, you want your collectionView can only select 1 cell at a time and if selected cell is clicked again, it will be deselected. If I'm misunderstanding anything, please tell me.

First

  • You shouldn't change textColor of day in didSelectItemAtIndexPath and didDeselectItemAtIndexPath methods. Because when you scroll collectionView, cells will be reused and color of day will be wrong for some cells.
  • To resolve it, using property selected of UICollectionViewCell.

    SmartCalendarDayCell.m

    - (void)setSelected:(BOOL)selected {
    [super setSelected:selected];

    if (selected) {
    self.day.textColor = [UIColor colorWithHexString:@"#D97E66" setAlpha:1];
    } else {
    self.day.textColor = [UIColor lightGrayColor];
    }
    }

Second

  • To deselect selected cell, you should check and do it on collectionView:shouldSelectItemAtIndexPath: method.

    - (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    if ([collectionView.indexPathsForSelectedItems containsObject:indexPath]) {
    [collectionView deselectItemAtIndexPath:indexPath animated:NO];
    return NO;
    }

    return YES;
    }

For more detail, you can check my demo repo here.

UICollectionView select and deselect

in viewDidLoad()

collectionView.allowsMultipleSelection = true;

afterword I implemented these methods

func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let cell = collectionView.cellForItemAtIndexPath(indexPath) as! MyCell
cell.toggleSelected()
}

func collectionView(collectionView: UICollectionView, didDeselectItemAtIndexPath indexPath: NSIndexPath) {
let cell = collectionView.cellForItemAtIndexPath(indexPath) as! MyCell
cell.toggleSelected()
}

finally in my class

class MyCell : UICollectionViewCell {

func toggleSelected ()
{
if (selected){
backgroundColor = UIColor.redColor()
}else {
backgroundColor = UIColor.whiteColor()
}
}

}

How to avoid uicollectionview deselect all items when new snapshot is applied?

After some digging, I found the root of the problem, and it lies inside Apple's code. At a minimum, I'd call that an "undocumented feature", some might say it's a bug.

When a snapshot is applied, the behavior of the UICollectionViewDiffableDataSource differs, whether the ItemIdentifierType is a value-type or a reference-type.

Of course, your ItemIdentifierType must conform to Hashable, but when calling apply(snapshot, animatingDifferences: true):

  • If ItemIdentifierType is a value-type (struct), then, its ==and hash functions are called, in order to compare the items of the newly received snapshot to the items of the currently shown snapshot, which allows to find the diff, animate differenes, and keep the selection if any (which was the original problem I asked help for).
  • If ItemIdentifierType is a reference-type (class), then, its ==and hash function are not called, which means that the items of the new snapshot and the currently shown snapshot are compare with bare reference equality (are pointers equal). As a consequence, if you have built your items "from scratch" with new instances having the same hash, then the comparison is always false, and you don't get the animations, and you loose the selection.

One easy work-around in this situation is to encapsulate your reference-type into a struct, which will forward == and hash to the class ones, and use this struct as the ItemIdentifierType

UICollectionView how to deselect all

Not all of the selected cells may be on screen at the point when you are clearing the selection status, so collectionView.cellForItemAtIndexPath(indexPath) may return nil. Since you have a force downcast you will get an exception in this case.

You need to modify your code to handle the potential nil condition but you can also make your code more efficient by using the indexPathsForSelectedItems property of UICollectionView

 let selectedItems = followCollectionView.indexPathsForSelectedItems
for (indexPath in selectedItems) {
followCollectionView.deselectItemAtIndexPath(indexPath, animated:true)
if let cell = followCollectionView.cellForItemAtIndexPath(indexPath) as? FollowCell {
cell.checkImg.hidden = true
}
}


Related Topics



Leave a reply



Submit