I want to check if the collectionview cell is clicked
func collectionView(_ collectionView: UICollectionView,
didSelectItemAt indexPath: IndexPath) {
//code..
}
Well you already have the key to your problem, it's just you don't know how to use it.
So every UIKit element has it's own delegate which is called when some event occurs.
In case of collection view, whenever cells are clicked didSelectItemAt will be called.
Here is an example
func collectionView(_ collectionView: UICollectionView,
didSelectItemAt indexPath: IndexPath) {
print("Cell \(indexPath.row + 1) clicked")
}
Considering above code just imagine you have 5 cells,
Whenever you click on any cell the above code will execute.
If you click on Third cell it will print: Cell 3 clicked
And if you don't click any cell nothing will happen.
Hope now everything is clear, Happy coding :)
Related
I have a collectionView that I set with this delegate and datasource:
extension CardView : UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
let numberOfURLS = cardModel?.urlStrings?.count
return numberOfURLS!
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let videoCell = collectionView.dequeueReusableCell(withReuseIdentifier: "videoCellIdentifier", for: indexPath)
videoCell.backgroundColor = UIColor.random()
return videoCell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return self.bounds.size
}
}
The problem is that the method cellForItemAtIndexpath is called only one time even if the cells returned are more than one.
The collectionView(_:cellForItemAt:) is called only as the cells appear or are about to appear. It won't call it for all of the cells, but only the visible ones (or those about to scroll into view). It is called in a just-in-time manner.
You can also turn on prefetching (which only works if you turn off “Estimate Size” in the collection view’s “Size inspector” IB or manually set the flow layout’s estimatedItemSize to .zero), but that only makes it a tiny bit more “eager” in terms of fetching cells (fetching those that are going to scroll into view a bit sooner than it would otherwise). It will not fetch all of the cells, but just those that the OS determines might possibly scroll into view soon.
so I am trying to change the background color of the collection view cell that is clicked, however, when I call the CollectionView.reloadData() method the backgrounds don't change. I am thinking that reloading the data only checks for adding and deleting cells, but does not actually call the configure function for each cell again. I was wondering how I can reconfigure these cells so that the background color can change for the selected cell. Thanks for the help in advance!
This is the method that I call:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
...
bagCollectionView.reloadData()
}
Instead of calling reloadData, you can try:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
cell?.backgroundColor = yourColor
}
Even though you do your logic in did select by finding a cell or implement logic in didSelectItemAt and didDeSelectItemAt, I suggest you should store the Index. Due to reusability of cell, there is chance that selected cell do not show color on scroll or if you have large number or records.
So save index in array(for multiple selections) or a single variable(single selection) and manage logic for the same.
For example:
cell.backgroundColor = selectedIndex == indexPath.item ? .red : .yellow
I am trying to implement a multi selection for UICollectionView. The delegate function didSelectItemAt is called but didDeselectItemAt is NEVER called and I do not know why? I am not even sure how it works. If I click on a cell - didSelectItemAt is called. So if I click once again on the same cell is didDeselectItemAt supposed to be called?
My UIViewController inherits and conforms to all of the following: UICollectionViewDelegate ,UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
viewdidLoad():
collectionView.allowsSelection = true
collectionView.allowsMultipleSelection = true
Delegate functions:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("didSelectItemAt")
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
print("DESELECT")
}
Please see this open issue on Github.
It could be that you have a tap gesture recognizer on a view in the same hierarchy that contains your collection view. Remove the tap gesture and see if that works. This happened to be my issue.
let tap = UITapGestureRecognizer(...) // your tap gesture recognizer
view.addGestureRecognizer(tap) // what you already have
tap.cancelsTouchesInView = false
I'm going to guess that your code is different from what you have shown us, and in that in your real code, you have not given the correct signature for didDeselect. Here's why. Look carefully at the code you have shown:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("didSelectItemAt")
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
print("DESELECT")
}
Now ask yourself: Why did the compiler permit the second func to stand, even though you forgot to say override? I'm guessing it's because it is not an override. There is something wrong with the signature, so it's just a meaningless function that doesn't conform to UICollectionViewDelegate.
Try using code completion to re-enter this function. If all goes well, it will be an override and it will start working.
To illustrate more precisely: This compiles, but the second method will never be called:
class CV : UICollectionViewController {
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("didSelectItemAt")
}
func colectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
print("DESELECT")
}
}
But this doesn't compile, because the signature is correct but we forgot override:
class CV : UICollectionViewController {
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("didSelectItemAt")
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
print("DESELECT")
}
}
But in that second one, if we do say override, it compiles and works.
It works like this:
if collectionView.allowsMultipleSelection = false // default then you have to tap on a different cell in order to deselect the previous one.
if collectionView.allowsMultipleSelection = true // only tapping on the same cell triggers deselect delegate method
I'm not sure if this changed recently, but with iOS13 it seems that simply specifying that a collection view cell is selected when the cell is dequeued is not enough for the collection view to allow you to deselect the cell. You need to also manually tell the collection view to select the cell.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: photoCellIdentifier, for: indexPath) as! YourCell
cell.isSelected = true
if cell.isSelected {
collectionView.selectItem(at: indexPath, animated: false, scrollPosition: .left)
}
}
I figured this out because i was able to select and deselect cells, but was not able to deselect a cell that was initially selected.
I am using UICollectionFlowLayout to establish minimumLineSpacing between collectionItems, I am trying to find a way to set the spacing to zero for some cells so there is no spacing, i.e they appear to be 'merged' while leaving others with their spaces intact, is it possible to make alterations in the cellForItemAt method for example to achieve this?
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "customColCell", for: indexPath) as! customColCell
if indexPath.row == 5 {
self.minimumLineSpacing = 100 // -- this does not result in an individual change...
}
cell.textLabel.text = indexPath.row.description
return cell
}
is it possible to make alterations in the cellForItemAt method for example to achieve this
No. What you're asking to do is not how a UICollectionViewFlowLayout behaves by default. You will need to write a collection view layout subclass.
I have a UICollectionViewController that is also acts as UICollectionViewDelegateFlowLayout to size cells. One cell selection modally presents some custom controls and then returns when completed, but the selected cell is no longer selected when it reappears.
I see delegate method for collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) being called, but not collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath). I also see the layout being asked for the preferred cell size, so I’m thinking they are being redrawn, but the cells already exist.
I can solve the problem by calling relaodData(), and the cell knows it is selected, and is redrawn accordingly, but that seems like a costly and wrong solution. Any suggestions about what I am missing? Thanks.
The default implementation of the following delegate method deselects the cell.
Override it without calling super:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) being called, but not collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath)
I see delegate method for collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) being called, but not collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath). I also see the layout being asked for the preferred cell size, so I’m thinking they are being redrawn, but the cells already exist.
Figured it out.
UICollectionViewController has property clearsSelectionOnViewWillAppear that is automatically set to true.
All I had to do was set it to false.