cell deselection needs double click - ios

I am getting trouble to deselect a cell in one click. As I click the cell, the label text inside it gets highlighted, and when i deselect the cell, 1st click deselects the highlighted label text color, turning it to black and then a 2nd click is needed to deselect the cell and leaving a trail of white colored text behind...I tried to disable the highlight feature of a label, but i am stuck here.You can see the setting for the label where it states color is white when highlighted.
I only implemented this func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
I also tried to set dateLabel.isHighlighted = false, but i am getting Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional
I have a dictionary with boolean value to track the state of a cell being selected or not, but this method doesnt solve my problem
Any help is appreciated and wish yall happy new years.
update:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
//addToList[currentMonthIndex].append(indexPath)
//dayCollectionView.deselectItem(at: indexPath, animated: true)
let cell = collectionView.cellForItem(at: indexPath) as! CustomCell
cell.dateLabel.isHighlighted = false
cell.dateLabel.isUserInteractionEnabled = false
}
I have successfully disabled both but i still need double click to deselect it. It seems like the label inside would take one click. I am sure of this because otherwise the cell would have been deselected on the 2nd click as my global array that tracks the cell selection status would have been changed by that click.

Two things:
In your UICollectionViewCell subclass:
override var isHighlighted: Bool {
willSet {
if newValue { // set selected state
} else { // set deselected state
}
}
}
And then I only implemented this
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
delegate call:
cell.isSelected = !cell.isSelected

Related

Wait for tap to end on UICollectionViewCell before setting isSelected to true

Currently, I have a collectionView and its only function is to let the user choose a color. When the user chooses a color (clicks a cell), a selected indicator (UIView) animates inside the cell, letting the user know that that certain color is selected. The core functionality is there, but my question is based more off of UI. When I scroll, the cell thinks I am clicking it and it sets isSelected to true for a second until I remove my finger from the cell. This causes an animation to happen at unwanted times. So, I think the problem is in shouldSelectItemAt in the CollectionViewDelegate methods. With UIGestureRecognizer I can measure whether or not the sender state has .began or .ended. Would my question call for this or something similar?
Here are the selection collectionView delegate methods.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = colorPreviewCollectionView.cellForItem(at: indexPath) as! TrayColorPreviewCell
// stuff with cell happens here
}
func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
let cell = colorPreviewCollectionView.cellForItem(at: indexPath) as! TrayColorPreviewCell
guard cell.isSelected else { return true }
return false
}
In the TrayColorPreviewCell (custom UICollectionViewCell) class, I use the isSelected to run the animation. This animation is the one that is happening repeatedly when scrolling.
override var isSelected: Bool {
didSet {
changeSelectionStatus()
}
}
func changeSelectionStatus() {
if isSelected {
// animation to indicate selection
} else {
// animation to indicate changed selection
}
}

Selected cell is deselected when switch to other page

I make a custom calendar using UICollectionView. From the calendar when I selected some dates and move forward to next month, then back again to the previous month, the selected item is deselected. Maybe it happens for reusable cell. How can I solve this problem.
For better understand what I want:
From September I select 4,5 then move to August/July/November (In this month maybe select some other dates or not)
Then return to September. In September I want to showed 4,5 as selected
I tried this using didSelectItemAt indexPath, but when return back to the September the selected item is deselected
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) as? CalendarDateRangePickerCell {
if cell.isSelected == true {
cell.backgroundColor = .blue
cell.label.textColor = .white
cell.isUserInteractionEnabled = true
}
selectedDate = cell.date!
}
}
First create an array of selected Cells. If you are using a model to set data to cell, you can create an array of selected models. or you can create an array of selected rows.
Let's say you are using a model.
var selectedDates: [DateModel] = []
Then
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) as? CalendarDateRangePickerCell {
selectedDate = cell.date!
if !selectedDates.contains(dataSourceModel[indexPath.row]) {
selectedDates.append(dataSourceModel[indexPath.row])
}
}
}
then in your cellForItem
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if selectedDates.contains(dataSourceModel[indexPath.row]) {
cell.isSelected = true
}
}
Also make sure you remove you model when unSelected
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
if selectedDates.contains(dataSourceModel[indexPath.row]) {
selectedDates.remove(dataSourceModel[indexPath.row])
}
}
*may contain some syntax error, but you can follow this path to get where you want.
Cells in UITableView and UICollectionView are reused when you scroll, that is why you should store which days selected in another place. Then, in cellForItem you should set isSelected.
Have you tried setting clearsSelectionOnViewWillAppear to false in viewDidLoad()?
A Boolean value indicating if the controller clears the selection when the collection view appears.
The default value of this property is true. When true, the collection view controller clears the collection view’s current selection when it receives a viewWillAppear(_:) message. Setting this property to false preserves the selection.
Source: Apple Developer Documentation

How to save the status of a cell so when I scroll or leave the page it doesn't refresh the cells?

I'm trying to make a store for a game where you can buy different colors of balls. I'm using a UICollectionView with all white balls to begin with, when I click a cell, it changes the white ball image to a colored ball image (EDIT: an image from a pre made array of colored images). when I scroll down and scroll back up, the cells I selected are reset to the white ball image. I don't want this obviously.
I've tried using the method already built into the UICollectionView class with didSelectItemAt but when I scroll down and back up it gets all messed up (When i select a cell a different one's image is changed not the correct one). I've tried using isSelected in the collectionViewCell class but I can't get the indexpath in here so I can't save which cells are selected.
override var isSelected: Bool{
didSet{
if self.isSelected
{
textImage.image = images[indexPath.item] // I don't know what to put here I don't have the indexPath
}
else
{
textImage.image = #imageLiteral(resourceName: "circleWhite")
}
}
}
Any help is great, I am fairly new to coding in Xcode so some explanation of what to do here is very much appreciated.
EDIT: I have an array of images that should be the store, not just one different color, multiple colors. When I click on a cell, it should access the image in the corresponding index in the array and use that image to replace the white circle.
Did the same thing in our code.Below is the solution for this.
1.Take an array of tuple to maintain selected status and specific colors or what ever you want.
var arrColor = [(isSelected:Bool,color:UIColor)]()
2. Now do the below code on cellForItemAt.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let collectionViewCell = self.iconCollectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as? EditDeviceCollectionViewCell else { return UICollectionViewCell() }
if arrColor[indexPath.item].isSelected{
arrColor[indexPath.item].color = .white
}else {
arrColor[indexPath.item].color = .black
}
return collectionViewCell
}
3.Now write the data source method and use below for color
//MARK:- UICollectionViewDelegate
extension yourViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
arrColor[indexPath.item].isSelected = true
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
arrColor[indexPath.item].isSelected = false.
}
}
Happy Coding 😊
When you scroll off the screen the cells get prepareForReuse called on them. What you need to do is store the state of the color somewhere else - like on the collectionView or a viewModel. And when cellForRow is called you pull the color to show for that row from the saved state variable.
Essentially what’s happening is the cells are being reused when they go off screen to save memory. So when you scroll back to them they are re-created often with the state of another cell since cells are reused.

What goes on behind the scenes in .reloadData() in terms of cell indexPath?

I'm using a collection view and every time I select the cell I change the cell background color to red. Simple enough:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)! as! CustomCell
cell.backgroundColor = .red
}
This works absolutely fine. When I select the top 3 cells going from left to right, the background color changes exactly as I expect:
However If I reload the collectionView after I select the cell the selection ordering begins to behave strangely. When I select the same top 3 cells in the same order from left to right, different cells become selected:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)! as! CustomCell
cell.backgroundColor = .red
collectionView.reloadData()
}
Apple's documentation is cryptic. https://developer.apple.com/documentation/uikit/uicollectionview/1618078-reloaddata
They say that "This causes the collection view to discard any currently visible items (including placeholders) and recreate items based on the current state of the data source object. " But this makes me think that upon calling reloadData() the collectionViewCells would go back to gray and not jump indexPaths.
Can anyone explain what is going on in reloadData() to make the cell selection at index path ordering so strange?
First You need to use dequeue cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier:"CustomCell", for: indexPath) as! CustomCell
And in cell you can use
-(void)prepareForReuse {
// Set default implementation
}

Swift - Selecting item in Collection View

I'm having a problem with selected items in a collection view.
Selected items change backgroundColor to blue, but it seems like the reusable cells are also affected.
my code looks like this:
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
var cell = collectionView.cellForItemAtIndexPath(indexPath) as UICollectionViewCell?
cell?.contentView.backgroundColor = UIColor.blueColor()
func collectionView(collectionView: UICollectionView, didDeslectItemAtIndexPath indexPath: NSIndexPath) {
var cell = collectionView.cellForItemAtIndexPath(indexPath) as UICollectionViewCell?
cell?.contentView.backgroundColor = UIColor.blackColor()
}
func collectionView(collectionView: UIControllerView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell: boxCell = collectionView.dequeueReusableCellWithReuseIdentifier("demoCell", forIndexPath: indexPath) as boxCell
cell.cellTitle.text = name[indexPath.row]
}
When I run the application, the selection works, selecting another cell, deselects the other selected cells, but when I scroll, the reusable cells are also turning blue.
I am using a horizontal scroll direction with only 1 row and 4 cells per row.
Where did I go wrong? Anybody else have had this issue?
It's normal behavior - the reason for that is that the items are reused, and after reusing they go through cellForItemAtIndexPath where you are not setting background color, so they keep last one you ever set - in your case didSelect method.
You should create a NSSet where you will keep all selected NSIndexPath's and add / remove NSIndexPath to it when selecting / deselecting.
Setting background color logic should be done in cellForItemAtIndexPath - after you check if NSIndexPath is existing in your NSSet you set a desired color.
On selection you will have to also reload certain element, so it will call cellForItemAtIndexPath for the one you clicked.
Use the prepare for resue method in your cell class.It will work fine.
override func prepareForReuse()
{
self.backgroundColor = UIColor.lightGrayColor()
}

Resources