Swift - 具有持续时间的 CoreAnimation

Swift - CoreAnimation with duration

试图了解 CoreAnimation 和完成处理程序。
为什么这段代码是一次性发生的,而不是在 5.0 秒的时间内发生?
我试图让它达到 运行 6 次,每次持续 5 秒,总共 30 秒,并“完成”每 5 秒打印一次。

    func animateBox() {

        UIView.animate(withDuration: 5.0, delay: 0.0, options: [], animations: {
            self.myBox.layer.borderColor = self.getRandom()
        }, completion: {finished in
            print("complete")
        })
    
    }

    @objc func buttonTapped() {
        
        for _ in 1...6 {
            animateBox()
            print("animate")
        }
    }

for _ in 1...6 { 被立即执行,所以 animateBox 被调用了 6 次,一个接一个,在一瞬间。

您要做的是调用每个动画块 前一个动画块的完成处理程序(在动画完成时调用)。像这样:

UIView.animate(withDuration: 5.0, delay: 0.0, options: [], animations: {
    self.myBox.layer.borderColor = self.getRandom()
}, completion: { finished in
    print("complete")

    UIView.animate(withDuration: 5.0, delay: 0.0, options: [], animations: {
        self.myBox.layer.borderColor = self.getRandom()
    }, completion: { finished in
        print("complete")

        UIView.animate(withDuration: 5.0, delay: 0.0, options: [], animations: {
            self.myBox.layer.borderColor = self.getRandom()
        }, completion: { finished in
            print("complete")

            ...
        })
    })
})

但这将导致一个巨大的完成金字塔...相反,请尝试使用 animateKeyframes:

let totalDuration = CGFloat(30)
let relativeIndividualDuration = CGFloat(1) / CGFloat(6)

UIView.animateKeyframes(withDuration: totalDuration, delay: 0, options: .calculationModeCubic, animations: {
    UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: relativeIndividualDuration) {
        self.myBox.layer.borderColor = self.getRandom()
    }

    UIView.addKeyframe(withRelativeStartTime: relativeIndividualDuration, relativeDuration: relativeIndividualDuration) {
        self.myBox.layer.borderColor = self.getRandom()
    }

    UIView.addKeyframe(withRelativeStartTime: relativeIndividualDuration * 2, relativeDuration: relativeIndividualDuration) {
        self.myBox.layer.borderColor = self.getRandom()
    }

    ...
})

另一种对动画进行排序的方法是使用延迟参数,并在第一步之后为每个步骤添加一个延迟:

func animateBox(step: Int) {
    let duration = 5.0
    UIView.animate(withDuration: duration, 
      delay: duration * Double(step), 
      options: [], 
      animations: {
        self.myBox.layer.borderColor = self.getRandom()
    }, completion: {finished in
        print("complete")
    })

}

@objc func buttonTapped() {
    
    for index in 0...5 {
        animateBox(step: index)
        print("animating step \(index)")
    }
}

(请注意,我将循环从 0...5 更改为 运行,因此 delay =step * duration 将从 0 开始。)