I have an array of cells in a collection view that I want to get to act like buttons.
When I tap on one, I want that cell to highlight, and then change colors.
I'm able to initially set the color of the view.backgroundColor inside of the collectionViewCell us the cellForItemAtIndexPath method. However, contrary to what I thought would work if I do this:
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let cell: ButtonCollectionCell = collectionView.dequeueReusableCellWithReuseIdentifier("ButtonCell", forIndexPath: indexPath) as! ButtonCollectionCell
cell.cellBackgroundView.backgroundColor = UIColor.darkGrayColor()
}
the color still isn't changing...
Essentially I want these cells to behave exactly like a button. I want them to highlight to a lighter gray color upon touch (they are initially white) after I release my finger I want them to become a dark gray color.
If I touch again I want them to again highlight to a lighter gray color, and then become white again.
If didSelectItemAtIndexPath isn't the way to do it, what is?
I would recommend some changes in your code that will help you to resolve your error.
First I would call the cellForItemAtIndexPath method to get your cell instead of using the dequeue method:
let cell = collectionView.cellForItemAtIndexPath(indexPath) as! ButtonCollectionCell
Then you should call the reloadItemsAtIndexPaths inside the didSelectItemAtIndexPath method to reload the cell:
collectionView.reloadItemsAtIndexPaths([indexPath])
Also you shouldn't change the background in the didSelectItemAtIndexPath method, but in the cellForItemAtIndexPath method where you check if the cell is selected:
if(cell?.selected){
//set your background-color
}else{
//change color
}
You can implement your own touch down gesture recognizer for the UICollectionView. See https://stackoverflow.com/a/15629234/1557276
When it's done, call -indexPathForItemAtPoint: method of the UICollectionView instance and then make changes in the cell returned by -cellForItemAtIndexPath:
When you dequeueReusableCellWithReuseIdentifier it can return a new cell to you.
Instead use cellForItemAtIndexPath to get the current cell at that index path.
Put this line of code I your method:
collectionView.cellForItem(at: indexPath)?.backgroundColor = UIColor.cyan
Related
My UICollectionViewCell is supposed to change its appearance when it comes into view (just a simple alpha/opacity change on one of the subviews). My code works fine when I scroll (vertically) slowly, but my attempt to access the cell (using cellForItemAt) returns nil when I scroll very quickly. Debugging + research reveals that cellForItem returns nil when the cell object isn't visible yet (even if the object is created), which is what happens when I scroll quickly.
What's the best way to to access the cell object when it's not visible yet?
From your description I'd say that the ideal way to access those cells is in function collectionView:willDisplayCell:forItemAtIndexPath:
That function tells you that the specified cell is just about to be displayed in the collection view.
So you can use it like this:
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
guard let cell = cell as? YourCell else { return }
// change alpha etc...
}
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.
This question already has answers here:
UICollectionView - didDeselectItemAtIndexPath not called if cell is selected
(8 answers)
Closed 5 years ago.
I'm working with a UICollectionView with the default selection rules (single selection only). In my custom UICollectionViewCell class, I've overridden isSelected like so:
override var isSelected: Bool {
didSet {
self.backgroundColor = isSelected ? UIColor.gray : UIColor.white
}
}
I also set isSelected for one specific cell in my view controller's collection​View(_:​cell​For​Item​At:​) because, for some reason, the collection's selectItem() method doesn't work in cellForItemAt.
This has allowed me to make sure that this one cell is selected and has its background changed before the entire view with the collection in it actually appears, but it's produced a weird problem.
When I start tapping other collection cells and selecting them, that one cell I set as selected from the start doesn't have its background color set to white.
However, as I proceed with selecting different cells in sequence by tapping them, the switching of their background colors to white happens just as intended, all the while that one cell where I set isSelected during the collection's initial population remains gray unless I specifically tap it and then tap away from it in a different cell.
Can anyone help me figure out why this is? Why isn't isSelected being set to false when I tap a cell other than the one whose isSelected property I set in cellForItemAt?
Edit: I tried implementing collection​View:​did​Deselect​Item​At​Index​Path:​, and it simply doesn't trigger. So on one hand, the isSelected property is definitely set, since the background for that one cell changes to gray, but on the other hand, the collection doesn't seem to perceive that cell as selected.
As you've edited your question, you can just use this approach.
// change background color when user touches cell
func collectionView(_ collectionView: UICollectionView, didHighlightItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
cell?.backgroundColor = UIColor.gray
}
// change background color back when user releases touch
func collectionView(_ collectionView: UICollectionView, didUnhighlightItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
cell?.backgroundColor = UIColor.white
}
I had assumed that it would be easy to add a background image to a UICollectionViewCell, but I am having trouble figuring it out. I have a border image like this that I want to add to every cell in my UICollectionView.
border.png
I couldn't see an option to add it in the Interface Builder and I'm not sure which method to use on UICollectionViewCell programmatically.
I have seen these similar questions:
UICollectionView Cell with Image, change Background with click (image is already added)
How to add a background image to UICollectionView that will scroll and zoom will cells (UICollectionView, not UICollectionViewCell)
I know this is a old question, with an accepted answer. But why not just set the border of the cell like so:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath)
cell.layer.borderWidth = 1.5
cell.layer.borderColor = UIColor.blueColor().CGColor
cell.layer.cornerRadius = 4
return cell
}
That way you don't need to use a image. Just adjust the borderWidth and cornerRadius until you get the desired effect.
let View=UIView()
View.backgroundColor=UIColor(patternImage:UIImage(named:"border.png")!)
cell.backgroundView=View
in uicollectionview cellForItemAtIndexPath method
Add a UIImageView to your CollectionViewCell and set this image as the image.
I've got a UICollectionView. With some cells inside with a white background color. I've set the selectedBackgroundView to a basic purple view.
My CollectionView has a constraint with a height of 0 and when I hit a button I update the constraint to 80. When I'm doing that, during the animation i can see the purple background on the screen until the end on the animation and i cannot understand why or how prevent this ?
Everything else working fine, it's just a "visual" bug.
Any suggestion about how to fix this ?
Gif of the bug where you can see the purple appearing during the animation
Here is my cell construction if it can be of any help :
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
var cell = collectionView.dequeueReusableCellWithReuseIdentifier("WidgetMenuCellIdentifier", forIndexPath: indexPath) as UICollectionViewCell
cell.removeSubviews()
// some code setup
cell.selectedBackgroundView = UIView()
cell.selectedBackgroundView.backgroundColor = UIColor.purpleColor()
return cell
}
Subclass your UICollectionViewCell
Do
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
// your code
cell.selectedBackgroundView.hidden = true
return cell
}
Then in your subclass :
override var selected:Bool {
willSet {
self.selectedBackgroundView.hidden = false
}
}
It should work.
It seems like this code is being executed within an animation, causing unexpected behavior at times based on how various properties animate. Another complicating factor is that, because cells are reused, it won't reproduce if a reused cell is already configured correctly (i.e. there is nothing to animate). Adding the following after styling the selectedBackgroundView was the least hacky solution I could think of.
[cell.selectedBackgroundView.layer removeAllAnimations];
Depending on what your cells are like you may also want to consider removing animations on other layers as well. For example:
[cell.backgroundView.layer removeAllAnimations];