'In UICollectionView, collapse animation is imperfect, with no height shrinking animation for the hidden item
Based on the problem described in
How to achieve smooth expand/ collapse animation in UICollectionView with dynamic cell height?
and the improvement mentioned in
Change default StackView animation
I had made the following changes
- Added an inner
UIView, for collapse and expand purpose - The inner
UIViewwill have attribute Clips to Bounds set to true - A vertical
UIStackViewplaced in the innerUIView, with bottom constraint set to high. - I really don't know. For unknown reason, the bottom constraint of the parent
UIStackViewneed to be low, in order for the UI to stay at top, during collapse animation.
This is my outcome.
As you can see,
- The expand animation works just fine!
- For collapse animation, the inner
UIViewwill disappear immediately, without any animation.
Do you have any idea, why there is no hidden animation during collapse?
Here's the code which perform collapse/ expand.
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return shops.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let collectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? CollectionViewCell else {
fatalError()
}
let shop = shops[indexPath.item]
collectionViewCell.title.text = shop.title
collectionViewCell._description.text = shop.description
if isExpanded[indexPath.item] {
collectionViewCell.innerView.isHidden = false
} else {
collectionViewCell.innerView.isHidden = true
}
return collectionViewCell
}
}
extension ViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
for i in (0..<isExpanded.count) {
if i == indexPath.item {
// ensure always visible
isExpanded[i] = true
} else {
// set all other rows to false
isExpanded[i] = false
}
if let c = collectionView.cellForItem(at: IndexPath(item: i, section: 0)) as? CollectionViewCell {
c.innerView.isHidden = !isExpanded[i]
}
}
collectionView.performBatchUpdates(nil, completion: nil)
}
}
What I have tried so far is
- Use a zero height constraint to activate/ deactivate, to replace
isHidden - Use
UIView.animate
But, the inner UIView will disappear immediately, without any height shrinking animation.
Do you have idea how I can fix this? Thanks.
Here's the code to illustrate the problem - https://github.com/yccheok/shop-dialog/tree/c399bca163096ad27de7de866af5d2de370a8afb
Solution 1:[1]
As I mentioned in a comment to you on another question, rarely do we find a "one size fits all" solution.
Instead of fighting with the default behaviors when setting .isHidden on a stack view's arranged subviews, here's a different approach.
Use two bottom constraints:
- One from the bottom of the "top / always-visible" UI elements
- One from the bottom of the "show/hide" elements (their container view)
Set the .priority of the second constraint to 750 (.defaultHigh).
When you want the cell to be "collapsed" set the .priority of the first constraint to 751 (.defaultHigh + 1).
When you want the cell to be "expanded" set the .priority of the first constraint to 749 (.defaultHigh - 1).
To animate the expand/collapse effect, wrap performBatchUpdates in a UIView.animate block.
I forked your GitHub repo here - https://github.com/DonMag/shop-dialog - and added this approach as "V2" so you can inspect it and see the differences.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | DonMag |

