UICollectionViewCell Deselect Pre-Selected Cell on First Tap - ios

I am modally presenting a viewcontroller that hosts a viewcollection with "pre-selected" cells formatted to a different background color based on data passed on segue.
When I tap on one of these "pre-selected" cells, it takes two taps to trigger the didDeselectItemAt delegate method. I understand why this is happening while debugging, where the cell although of different color is not necessarily recognized in a selected state. Is there any way to trigger didDeselectItemAt first for the "pre-selected" cells?
I've tried, within the delegate method cellForItemAt, to incorporate, as part of a conditional statement that changes the cell background color, setting cell.isSelected = true. Similarly within the same delegate method, I've also tried invoking a function that would invoke the delegate method didSelectItemAt with indexPaths of these "pre-selected" cells. Both produced the same result.
Below is (abbreviated) relevant code snippets:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath as IndexPath) as! ExampleCollectionViewCell
if preselectedDataPoints { cell.backgroundColor = blue }
else { cell.backgroundColor = white }
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
cell?.backgroundColor = blue
preselectedDataPoints.append(newDataPoint)
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
cell?.backgroundColor = white
preselectedDataPoints.remove(at: existingDataPoint)
}

Programatically call collectionView.deselectItem(at: indexPath, animated: true) in didSelectItem if cell is preselected.
Refrer code
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if preselectedDataPoints {
collectionView.deselectItem(at: indexPath, animated: true)
}else{
let cell = collectionView.cellForItem(at: indexPath)
cell?.backgroundColor = blue
preselectedDataPoints.append(newDataPoint)
}
}
or
Directly call what ever code you need to execute in deSelect method
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if preselectedDataPoints {
let cell = collectionView.cellForItem(at: indexPath)
cell?.backgroundColor = white
preselectedDataPoints.remove(at: existingDataPoint)
}else{
let cell = collectionView.cellForItem(at: indexPath)
cell?.backgroundColor = blue
preselectedDataPoints.append(newDataPoint)
}
}

Related

How to change the color of View in collection view

How to change the color of view in collection view when it select and when the select the other view before view came to original color How to set it?
You can use the didSelectItemAtIndexPath method and the didDeselectItemAtIndexPath method from the UICollectionViewDelegate protocol. UICollectionViewDelegate is an Objective-C protocol, indicated by #objc, that allows you to have optional methods like didSelectItemAtIndexPath and didDeselectItemAtIndexPath that you can choose to implement optionally in your delegate.
When UICollectionView gets instantiated initially, it goes through all the available methods you've implemented in the instance of UICollectionViewDelegate with responds(to:), which takes a selector parameter and returns a bool, to see which methods from the UICollectionViewDelegate protocol you've implemented without actually sending a message to them. As it detects the following methods, it remembers them and calls those methods whenever you select or deselect a cell from the collection view:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
if let indexPaths = collectionView.indexPathsForSelectedItems, let firstIndex = indexPaths.first {
cell.backgroundColor = firstIndex == indexPath ? .white : .gray
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) {
cell.backgroundColor = .white
}
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
guard let cell = collectionView.cellForItem(at: indexPath) else {
return
}
cell.backgroundColor = .gray
}
Alternatively, you can use the collectionView(_:didHighlightItemAt:) method and the collectionView(_:didUnhighlightItemAt:) method. The following sample code is from here:
func collectionView(_ collectionView: UICollectionView, didHighlightItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) {
cell.contentView.backgroundColor = #colorLiteral(red: 1, green: 0.4932718873, blue: 0.4739984274, alpha: 1)
}
}
func collectionView(_ collectionView: UICollectionView, didUnhighlightItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) {
cell.contentView.backgroundColor = nil
}
}
You can simply add two methods for changing colour of the view or cell in the collectionView:-
//When cell got selected
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath){
cell.backgroundColor = .green
}
}
//When cell deselected
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath){
cell.backgroundColor = .clear
}
}
If you have view inside your cell as backgroundView then you can use same functions and just need to replace cell.backgroundView.backgroundColor in place of the cell.backgroundColor. Just remember "backgroundView" is the name of the view you have inside cell.
If you want more information about this then you can follow below attached links:-
1). https://www.tutorialfor.com/questions-296884.html
2). https://en.it1352.com/article/fccf358a040b4ddba872d7324602754c.html

Deselecting the cell in cellForItemAt when selecting cell in didSelectItemAt

I have a variabel selectedIndexPath, which gets the indexPath selected from previous view controller. I am getting the required backgroundColor for cell in the collection view of present view controller. But when I select another cell in the collection view, the selected cell of previous view controller remains same without being deselected. So, now I have two cells with the background color. Following is my code
var selectedIndexPath = IndexPath()
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView.allowsMultipleSelection = false
self.collectionView.allowsSelection = true
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! TestCollectionViewCell
if (indexPath == selectedIndexPath)
{
cell.backgroundColor = UIColor.white
}
else
{
cell.backgroundColor = UIColor.clear
}
collectionView.allowsMultipleSelection = false
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
let cell = collectionView.cellForItem(at: indexPath)
cell?.backgroundColor = UIColor.white
collectionView.allowsMultipleSelection = false
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath)
{
let cell = collectionView.cellForItem(at: indexPath)
cell?.backgroundColor = UIColor.clear
collectionView.allowsMultipleSelection = false
}
How do I deselect the cell in cellForItemAt, when I select new cell in didSelectItemAt. Thanks in advance.
First of all set allowsMultipleSelection in Interface Builder and remove all redundant occurrences of setting it in code
You have to update the selectedIndexPath variable. Manipulating the cell directly is always a bad idea.
Reloading the cells is much more reliable.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
guard selectedIndexPath != indexPath else { return }
let indexPathsToUpdate = [selectedIndexPath, indexPath]
selectedIndexPath = indexPath
tableView.reloadRows(at: indexPathsToUpdate, with: .none)
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath)
{
guard selectedIndexPath == indexPath else { return }
selectedIndexPath = IndexPath()
tableView.reloadRows(at: [indexPath], with: .none)
}
There is only one problem: If you want to have an empty selection you have to declare selectedIndexPath as optional and handle it properly.
First of all, you don't need to override didDeselect for this. All you need to do is deselect the previously selected item while selecting the current item in didSelectItem. For that you can choose to maintain states in the cell as:
func changeToSelectedState() {
self.backgroundColor = UIColor.white
}
func changeToUnselectedState() {
self.backgroundColor = UIColor.clear
}
Or you can choose to write the same in the controller itself. And then you need to perform the deselection and selection in the following manner.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
let previousSelectedCell = collectionView.cellForItem(at: selectedIndexPath)
previousSelectedCell.changeToUnselectedState()
let currentCell = collectionView.cellForItem(at: indexPath)
currentCell.changeToSelectedState()
selectedIndexPath = indexPath
}

UIcollectionview list of horizontal toggle button in Swift

I would like to create a list of horizontal buttons in UIcollectionview. Only one and only button can be selected and it will change to red color, problem is the selected cell will be reuse then there is possibility two red buttons appear in a same time, what should I do to solve this?
Store the index of the selected row in didSelectRow , then inside cellForItemAt
var selectedIndex = -1
var urlArr = [url1,url2,url3,url4,url5]
//
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CellIdentifier1, for: indexPath as IndexPath) as! imagesCollectionViewCell
let currentUrl = urlArr[indexPath.row]
if(indexPath.row == selectedIndex)
{
// red
}
else
{
// default
}
}
if you want to create multiple toggle cells in UICollectionView then use this.
var selectedIndexes = NSMutableArray()
var items = [item1,item2,item3,item4,item5]
in cellForItemAt method. check for selected indexPath.row
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: yourIdentifier, for: indexPath as IndexPath) as! yourCell
if selectedIndexes.contains(indexPath.row){
// make it active
}else{
// make it default
}
}
And in didSelectItemAt method add and remove indexPath from the Array to toggle the button of cell
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if selectedIndexes.contains(indexPath.row){
selectedIndexes.add(indexPath.row)
}else{
selectedIndexes.remove(indexPath.row)
}
collectionView.reloadData()
}

ios UICollectionView cell selecting and deselecting issue

Im using UIcollection view as my tabbar
when I scroll collection view horizontally previous selected cell will not deselect when i select new one
this is my code to change colour when i select a cell and deselect a cell
var selectedIndexPath : IndexPath = []
func collectionView(_ collectionView: UICollectionView,
didSelectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) as?
BottomCollectionViewCell {
cell.contentView.backgroundColor = UIColor.orange
cell.backgroundColor = UIColor.orange
}
if let preViousSelectedcell = collectionView.cellForItem(at:
selectedIndexPath) as? BottomCollectionViewCell {
preViousSelectedcell.contentView.backgroundColor=UIColor.purple
preViousSelectedcell.backgroundColor = UIColor.purple
}
selectedIndexPath = indexPath
}
while scrolling cells are reused that time cellForItemAt will call so you need to change some modification in your code
func collectionView(_ collectionView: UICollectionView,
didSelectItemAt indexPath: IndexPath) {
selectedIndexPath = indexPath
YOUR_COLLECTION_VIEW.reloadData()
}
and add below lines inside your collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath)
if indexPath == selectedIndexPath {
cell.contentView.backgroundColor=UIColor.purple
cell.backgroundColor = UIColor.purple
} else {
cell.contentView.backgroundColor = UIColor.orange
cell.backgroundColor = UIColor.orange
}
Hope this will help you

didDeselectItemAt indexPath doesn't triggered when another cell is selected

I'm trying to implement this feature: in my app, if I selected a cell in UICollectionView, then the borders becomes blue, and if I select another one, the previous should be deselected and borders should become transparent. There is methods that I've written:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! ChatCell
/* Set some settings */
if globalSelected[indexPath.item] {
cell.circleView.layer.borderColor = UIColor.blue.cgColor
} else {
cell.circleView.layer.borderColor = UIColor.clear.cgColor
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
//Global variable for maintain selection
global.selectedChatPath = indexPath
globalSelected[indexPath.item] = true
collectionView.reloadData()
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
if indexPath != nilPath {
globalSelected[indexPath.item] = false
collectionView.reloadData()
}
}
The nilPath is just IndexPath(item: -1, section: 0), but it doesn't matter, because collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) isn't even called. My CollectionView has allowSelection = true and allowsMultipleSelection = false properties. I will be thankful for any help.
If only a single cell is supposed to be selected at the same time I recommend to put the currently selected index path into a instance variable (nil means nothing is selected)
var selectedIndexPath : IndexPath?
In cellForItemAtset the colors depending on the instance variable
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! ChatCell
/* Set some settings */
if let selected = selectedIndexPath, selected == indexPath {
cell.circleView.layer.borderColor = UIColor.blue.cgColor
} else {
cell.circleView.layer.borderColor = UIColor.clear.cgColor
}
return cell
}
In didSelectItemAt reload only the previous and new selected cells and set selectedIndexPath to the new selected index path. This is more efficient than reloading the entire collection view.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
//Global variable for maintain selection
var cellsToReload = [indexPath]
if let selected = selectedIndexPath {
cellsToReload.append(selected)
}
selectedIndexPath = indexPath
collectionView.reloadItems(at: cellsToReload)
}
didDeselectItemAt is only needed if you want to deselect a cell explicitly.
Try this
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! ChatCell
/* Set some settings */
if globalSelected[indexPath.item] {
cell.circleView.layer.borderColor = UIColor.blue.cgColor
collectionView.selectItemAtIndexPath(indexPath, animated: false, scrollPosition: .None)
}
else {
cell.circleView.layer.borderColor = UIColor.clear.cgColor
collectionView.deselectItemAtIndexPath(indexPath, animated: false)
}
return cell
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) as! CollectionViewCell
if selectedIndex == indexPath.item {
cell.backgroundColor = .blue
} else {
cell.backgroundColor = .clear
}
}
and in didSelectItemAt,
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedIndex = indexPath.item
collectionView.reloadData()
}
Reload your UICollectionView everytime a cell is selected and then change the border of the desired cell.
When you reload data the border for the previous cell is removed, after which you can add border to the cell that you want.
didDeselectItem won't be called until you tap the selected cell again. In order to deselect the previously selected cell when you tap another cell, you need to set the previously selected cell's setting in the global variable to false in didSelectItem like the following:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
globalSelected[global.selectedChatPath.item] = false //Set the previously selected cell's setting to false
//Global variable for maintain selection
global.selectedChatPath = indexPath
globalSelected[indexPath.item] = true
collectionView.reloadData()
}

Resources