Magrafear
Magrafear

Reputation: 573

Toggle image in UICollectionViewCell when cell selected

I have a UICollectionView of items, and I would like an image in a cell to be toggled when the user selects the cell.

I have a custom UICollectionViewCell:

class RDCell: UICollectionViewCell {

var textLabel: UILabel!
var imageView: UIImageView!
var isSelected: Bool!

...(do init and all that good stuff)

}

And selctected item in collection view :

func collectionView(collectionView: UICollectionView, shouldSelectItemAtIndexPath indexPath: NSIndexPath) -> Bool {

    let celld = (collectionView.dequeueReusableCellWithReuseIdentifier("collectionCell", forIndexPath: indexPath) as? RDCell)!

    let indexPaths = collectionView.indexPathsForSelectedItems()

    let newCell = collectionView.cellForItemAtIndexPath(indexPath) as! RDCell
    if(celld.selected){

        var image: UIImage = UIImage(named: "notSelected")!
        newCell.imageView.image = image

        newCell.selected = false
    }else{
        var image: UIImage = UIImage(named: "selected")!
        newCell.imageView.image = image

        newCell.selected = true
    }

return true
}

My attempt partially works, after selecting and unselecting one item I am not able to select it again after, I need to select a different cell before returning to select the first, and this same bug happens on all selected cells.

Any suggestions or another way to implement the functionality I seek would be greatly appreciated.

Upvotes: 2

Views: 2299

Answers (4)

Shahriyar
Shahriyar

Reputation: 698

Swift 4/5:

Inside your collectionViewCell class define a variable called imageName then override isSelected property to set the image name based on selected state and in collectionView cellForItemAt method set the value for imageName variable for each cell.

CollectionViewCell Class:

class YourCollectionViewCell: UICollectionViewCell
{

    @IBOutlet var cellIcon: UIImageView!

    var imageName = ""

    override var isSelected: Bool {
        didSet {
            if self.isSelected {
                self.cellIcon.image = UIImage(named: "\(self.imageName) Highlighted")
            } else {
                self.cellIcon.image = UIImage(named: "\(self.imageName) Unhighlighted")
            }

        }
    }
}

CollectionView DataSource:

class YourCollectionVewController: UICollectionViewDataSource 
{

     func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        // this line is an extension don't copy paste
        let cell = collectionView.dequeueReusableCell(with: YourCollectionViewCell.self, for: indexPath) 

        let imageName = "yourImageName Unhighlighted"
        cell.cellIcon.image = UIImage(named: imageName)
        cell.imageName = imageName

        return cell
    }
}

Upvotes: 0

Andres Paladines
Andres Paladines

Reputation: 1218

Swift 4

I just use the property "highlightedImage" in the "cellForItemAt indexPath" method of the collectionView and set another image on it.

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)

    cell.backgroundColor = UIColor.clear

    let imageView = cell.viewWithTag(1) as! UIImageView
    imageView.image = imagesArray1[indexPath.row]
    imageView.highlightedImage = imagesArray2[indexPath.row]
    imageView.contentMode = .scaleAspectFill
    return cell
}

In my case, i assigned a tag in the imageView and call it through it.

Best regards.

Upvotes: 2

Haris Zaheer
Haris Zaheer

Reputation: 3

My approach will be different for this, i would have used the didDeselectItemAt method of the collectionView...

Swift 3:

func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
    let newCell = collectionView.cellForItem(at: indexPath)
    newCell.imageView.image = imageAfterSelection[indexPath.row]
}


func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {


    let newCell = collectionView.cellForItem(at: indexPath) 

    newCell.imageView.image = imagesAfterDeselection[indexPath.row]

}

Upvotes: 0

GetSwifty
GetSwifty

Reputation: 7756

You should never call cellForItemAtIndexPath directly. You have no guarantee of what cell you're getting and the changes you make may have no effect.

The proper way to do this is to track your state within the class and change the state of the cell in cellForItemAtIndexPath. Then you simply call collectionView?.reloadData() when you need to update the views.

Simple example for reference:

var selectionTracking: [[Bool]] = []


func collectionView(collectionView: UICollectionView, shouldSelectItemAtIndexPath indexPath: NSIndexPath) -> Bool {

    if let selected = selectionTracking[indexPath.section][indexPath.row] {
        return selected
    }
    else{
        selectionTracking[indexPath.section][indexPath.row] = false
    }
    return true
}

override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {

    if let selected = selectionTracking[indexPath.section][indexPath.row] {
        selectionTracking[indexPath.section][indexPath.row] = !selectionTracking[indexPath.section][indexPath.row]
    }
    else{
        selectionTracking[indexPath.section][indexPath.row] = true
    }

    collectionView?.reloadData()
    return true
}

Upvotes: 1

Related Questions