Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shadow/Cell flickers when animating the height

I am having an issue with the shadow on my cells (tap GIF to see animation):

enter image description here

The shadow flickers when I call beginUpdates:

cell.tappedParentView
    .subscribe(onNext: { [weak self] in
        guard let self = self else { return }
        cell.animate()
        self.tableView.beginUpdates()
        self.tableView.endUpdates()

        self.expandedIndexPaths[indexPath] = !self.expandedIndexPaths[indexPath].or(false)
    })
    .disposed(by: cell.cellBag)

The animation code animate in the cell is as follows:

func animate() {
    spacerViewHeightConstraint?.constant = isExpanded ? 0.0 : 8.0

    parentView.updateLayout(isExpanded: !self.isExpanded)
    UIView.animate(withDuration: 0.2) {
           self.childViews.forEach {
               $0.isHidden = self.isExpanded
               $0.alpha = self.isExpanded ? 0.0 : 1.0
           }

         self.layoutIfNeeded()
    }

    isExpanded.toggle()
}

And updateLayout:

func updateLayout(isExpanded: Bool, animated: Bool = true) {
    stackViewLeadingConstraint.constant = isExpanded ? 8.0 : 16.0
    stackViewTrailingConstraint.constant = isExpanded ? 8.0 : 16.0
    stackViewTopConstraint.constant = isExpanded ? 8.0 : 16.0
    stackViewBottomConstraint.constant = isExpanded ? 0.0 : 16.0

    let layout = {
        self.layoutIfNeeded()

        self.shadow = isExpanded ? nil : .card
        self.backgroundImageView.alpha = isExpanded ? 0.0 : 1.0
        self.containerView.backgroundColor = isExpanded ? .clear : .red
        self.titleLabel.textColor = isExpanded ? Theme.text100 : .white
        self.descLabel.textColor = isExpanded ? Theme.text100 : .white

        self.startingLabel.alpha = isExpanded ? 0.0 : 1.0
        self.startingLabel.isHidden = isExpanded

        self.priceLabel.alpha = isExpanded ? 0.0 : 1.0
        self.priceLabel.isHidden = isExpanded

        self.showLessButton.isHidden = !isExpanded
        self.showLessButton.alpha = isExpanded ? 1.0 : 0.0

        self.stackView.spacing = isExpanded ? 8.0 : 4.0
    }

    if animated {
        UIView.animate(withDuration: 0.2) { layout() }
    } else {
        layout()
    }
}

If I perform the animation without calling self.tableView.beginUpdates() and self.tableView.endUpdates() the flicker doesn't happen but then of course the cell height does not adjust accordingly. I'm really not sure how to fix this. All the views and cells have transparent backgrounds (only the table view background is a solid color). clipToBounds is off for the view and contentView in each cell/view. I have also tried using solid colors as the backgrounds but this doesn't seem to make any difference. How can I fix this?

Any pointers would be greatly appreciated! Thanks!

like image 248
Kex Avatar asked Apr 24 '26 22:04

Kex


1 Answers

My answer is inspired from this one here

It took me 2 days to find the answer. Finally, I noticed that when I was calling tableView.reloadRows(at: [indexPath], with: .fade) or just simply tableView.beginUpdates() and tableView.endUpdates() and if pausing the view with the Debug View Hierarchy in XCode, for some reasons the cell had its clipsToBounds = true during the reloading process only, even if I explicitly set it to false.

The solution which worked for me is this:

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    cell.clipsToBounds = false
    cell.layer.masksToBounds = false
    cell.contentView.layer.masksToBounds = false
}
like image 150
Starsky Avatar answered Apr 27 '26 12:04

Starsky