iosswiftuicollectionviewuiviewanimationtransition

Animate isHighlighted state of an imageView inside a cell of a collection view


I am stumbling into an issue with isHighlighted state of an imageView inside a cell of a collection view: I cannot manage to animate the transition between 2 images the way I aim for.

Each custom cell of my collection view has 3 views:

  1. an UIImageView : a background image with highlighted and non highlighted related .pngs
  2. a second UIImageView : another image with highlighted and non highlighted related .pngs as a subview of the previous UIImageView
  3. a custom UILabel : a text label

When I touch the cell, both ImageViews instantly move to highlighted state but I would like them to transition to this state instead (a simple CrossDissolve transition)

I have tried 2 different approaches : overriding isSelected method in the custom cell class and putting the animation in collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)

Overriding isSelected property:

    override var isSelected: Bool {
        didSet {
            UIView.transition(with: cellImageView, duration: 1, options: .transitionCrossDissolve, animations: {
                self.cellImageView.setNeedsLayout()
                self.cellImageView.layoutIfNeeded()
                self.cellImageView.isHighlighted = self.isSelected
            })
        }
    }

With this way, I get no animation when I click on the cell in the simulator, either when the cell is selected or when it loses it selected state, except when I click and hold a cell: then I get an animation of the previous cell losing its selected cell (in other words I have the transition of the previous selected imageView from isHighlighted(true) to isHighlighted(false)).

didSelectItemAt indexPath Method

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

        let cell = collectionView.cellForItem(at: indexPath) as! SubMenuCell
        UIView.transition(with: cell, duration: 1.0, options: .transitionCrossDissolve, animations: 
            cell.setNeedsLayout()
            cell.layoutIfNeeded()
            cell.cellImageView.isHighlighted = cell.isSelected
        })
    }

This method does not trigger an animation when the cell is selected but does provide a transition animation when the cell moves from isSelected(true) to isSelected(false): in other words, I see the previous selected cell fading out and I don't see the selected cell fading in.

I'd be grateful for any tips as to why it does not work and how it could be fixed!

Thank you all for the time you took to read and reply! Best,


Solution

  • Thanks to the guys to have taken the time to think about my question. By investigating further, I decided to check how and when 3 events, namely cell.isHighlighted, cell.isSelected and collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) where triggered and I found some pretty interesting things actually. Let me sum it up:

    When you touch a cell (cell1) in a collection view, it appears the following events occur:

    1. cell1.isHightlighted is set to true
    2. cell1.isHightlighted is set to false
    3. cell1.isSelected is set to true, which calls back cell1.isHighlighted property
    4. and lastly collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) method is called

    Now when you touch another cell (cell2) in the collection View, the following events occur:

    1. cell2.isHighlighted is set to true
    2. cell1.isSelected is set to false, which calls back cell1.isHightlighted property
    3. cell2.isHighlighted is set to false
    4. cell1.isSelected is set to false again (!)
    5. cell2.isSelected is set to true, which calls back cell2.isHighlighted property
    6. and again lastly collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) method is called

    With that in mind, I came to the conclusion that to run a transition animation on isHighlighted state in the collection view method will never work as intended since the property has already been set prior to reach the method.

    One solution to obtain both animations (isHighlighted true and false) that I have found is to act on the property observers of isHighlighted and isSelected of my custom collection view cell, as follow:

    override var isHighlighted: Bool {
            willSet {
                if newValue == true {
                    UIView.transition(with: cellImageView, duration: 1.0, options: .transitionCrossDissolve, animations: {
                        self.cellImageView.isHighlighted = newValue
                    })
                }
            }
        }
        
     override var isSelected: Bool {
            willSet {
                if newValue == false {
                    UIView.transition(with: cellImageView, duration: 1.0, options: .transitionCrossDissolve, animations: {
                        self.cellImageView.isHighlighted = newValue
                    })
                }
            }
        }

    I hope it helps, and thanks again to all of you whom have spent some time trying to figuring it out! If anyone finds a more elegant solution, do not hesitate to post it!!! Best,