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
ofday
indidSelectItemAtIndexPath
anddidDeselectItemAtIndexPath
methods. Because when you scrollcollectionView
, cells will be reused and color ofday
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==
andhash
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==
andhash
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
Swift - How to Get Last Taken 3 Photos from Photo Library
Show Search Bar in Navigation Bar Without Scrolling on iOS 11
Ios7 Safari: Saving to Home-Screen and Persist Token
Get iOS Itunes App Store Id of an App Itself
Flutter iOS Build Failed an Error of Pod Files: Podfile Is Out of Date
Crashlytics iOS - Log Caught Exception
Uitextfield Should Accept Number Only Values
Showing a Uipickerview with Uiactionsheet in iOS8 Not Working
Xcode 8 Objective-C Category Warning
Stretch Background Image for Uibutton
How to Get Selected Value from Uipickerview
View at the Bottom in a Uiscrollview, with Autolayout
Swapping Child Views in a Container View
Why Does Uicollectionview Log an Error When the Cells Are Fullscreen
iOS 11 Core Nfc - Any Sample Code
Swift Failed with Exit Code 1 While Compiling in Xcode - Possibly Related to Bridging-Headers