1
votes

Basically, I have a UITableView where that I want to animate when a button is tapped. Every cell should be scaled by 1.10 and return to their transform identity. So that's two different animations for each cell that I doing with UIView.animateKeyframesWithDuration.

But I want to have them slightly delayed from each other. So I call this method in a loop like this:

for (index, cell) in tableView.visibleCells.enumerate() {
    let delay = 0.1 * Double(index)

    let duration = 0.5

    UIView.animateKeyframesWithDuration(duration, delay: delay, options: [], animations: {
        UIView.addKeyframeWithRelativeStartTime(0.0, relativeDuration: duration * 1/2, animations: {
            cell.transform = CGAffineTransformScale(cell.transform, 1.10, 1.10)
        })
        UIView.addKeyframeWithRelativeStartTime(1/2, relativeDuration: duration * 1/2, animations: {
            cell.transform = CGAffineTransformIdentity
        })
        }, completion: nil)
}

The problem is that the completion part works only for each animation.

What I'm looking for is a way to do something once every animation is completed. How can I achieve that?

1
Don't you think that there is a contradiction in your question? The completion block can be invoked for each animation, and you want to do something once every animation is completed... ?? Do you mean that you want to invoke a method for each cell (after 2 keyframe animations on the cell completed)? - Ducky
Okay let's pretend there is only one animation per object. I just want to invoke a method once all the animations are done. I thought about creating a counter with the number of animations to perform, decrement it at every animation's block completion and execute my method once it gets to 0 by checking it with a didSet {} but I was wondering if there was a proper way to do it. - Nico

1 Answers

3
votes

You could wrap everything inside a UIView.animateKeyframesWithDuration call and call a single completion block like this:

let delay = 0.1
let duration = 0.5
let totalDuration = duration + TimeInterval(tableView.visibleCells.count - 1) * delay

UIView.animateKeyframes(
    withDuration: totalDuration/2,
    delay: 0,
    options: [],
    animations: {
        for (index, cell) in self.tableView.visibleCells.enumerated() {
            let relativeDelay = delay * Double(index)

            print("Cell \(index) animation starts after relative delay: \(relativeDelay)")

            UIView.addKeyframe(withRelativeStartTime: relativeDelay, relativeDuration: duration/2, animations: {
                cell.transform = cell.transform.scaledBy(x: 1.10, y: 1.10)
            })
            UIView.addKeyframe(withRelativeStartTime: relativeDelay +  duration/2, relativeDuration: duration/2, animations: {
                cell.transform = CGAffineTransform.identity
            })
        }
}, completion: { finished in
    if finished {
        print("Done")
    }
})

I hope this is what you want.