1

I have a subclassed UICollectionViewCell and I want it to expand when tapped.

To achieve this, I put the title into a view ("titleStack") and the body into a separate view ("bodyStack"), and then put both of them into a container UIStackView ("mainStack"). I then constrain the contentView of the cell to the leading, trailing, and top edges of mainStack.

When the cell is selected, a constraint is applied that sets the bottom of the contentView's constraint to be the bottom of bodyStack. When it's unselected, I remove that constraint and instead apply one that sets the contentView's bottom constraint equal to titleStack's bottom constraint.

For the most part this works well, but when deselecting, there's this little jump, as you can see in this video:

enter image description here

What I would like is for titleStack to stay pinned to the top while the cell animates the shrinking portion, but it appears to jump to the bottom, giving it a sort of glitchy look. I'm wondering how I can change this.

I've pasted the relevant code below:

private func setUp() {
    backgroundColor = .systemGray6
    clipsToBounds = true
    layer.cornerRadius = cornerRadius

    setUpMainStack()
    setUpConstraints()
    updateAppearance()
}

private func setUpMainStack() {
    
    contentView.constrain(mainStack, using: .edges, padding: 5, except: [.bottom])
    mainStack.add([titleStack, bodyStack])
    
    bodyStack.add([countryLabel, foundedLabel, codeLabel, nationalLabel])
}

private func setUpConstraints() {
    titleStack.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
    
    closedConstraint =
        titleStack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
    closedConstraint?.priority = .defaultLow // use low priority so stack stays pinned to top of cell
    
    openConstraint =
        bodyStack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
    openConstraint?.priority = .defaultLow
}

/// Updates the views to reflect changes in selection
private func updateAppearance() {

    
    UIView.animate(withDuration: 0.3) {
        self.closedConstraint?.isActive = !self.isSelected
        self.openConstraint?.isActive = self.isSelected
    }
}

Thanks so much!

2
  • 1
    Instead of setting the constraints active/inactive, can you try animating the constraint values like this? stackoverflow.com/a/25650669/4490923 Commented May 29, 2022 at 17:31
  • That didn't quite work in my case, but it led me down the right path so thank you! Commented May 30, 2022 at 18:46

1 Answer 1

0

I was able to solve this by simply showing and hiding my "bodyStack" as well as using "layoutIfNeeded." I removed closedConstraint and openConstraint and just gave it a normal bottom constraint.

The relevant code:

func updateAppearance() {

        UIView.animate(withDuration: 0.3) {
            
            self.bodyStack.isHidden = !self.isSelected
            self.layoutIfNeeded()
        }
    }
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.