I have some animations (testable in a playground):
import UIKit
import XCPlayground
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution
let view = UIView()
view.frame = .init(x: 0, y: 0, width: 150, height: 150)
view.backgroundColor = .orange
let hand = UIView()
view.addSubview(hand)
hand.frame = .init(x: 0, y: 0, width: 10, height: 10)
hand.center = view.center
hand.backgroundColor = .green
PlaygroundPage.current.liveView = view
let fadeDuration: TimeInterval = 0.4
let translationDuration: TimeInterval = 1
let resetDuration: TimeInterval = 0.25
let duration: TimeInterval =
7 * fadeDuration +
4 * translationDuration +
3 * resetDuration
var currentTime: TimeInterval = 0.0
func addKey(_ animations: @escaping () -> Void, animationTime: TimeInterval) {
UIView.addKeyframe(withRelativeStartTime: currentTime / duration, relativeDuration: animationTime / duration, animations: animations)
currentTime += animationTime
}
func fadeIn() {
addKey({
hand.alpha = 1
}, animationTime: fadeDuration)
}
func fadeOut() {
addKey({
hand.alpha = 0
}, animationTime: fadeDuration)
}
func translate(_ direction: (CGFloat) -> CGFloat) {
let x = direction(50)
addKey({
hand.transform = .init(translationX: x, y: 0)
}, animationTime: translationDuration)
}
func reset() {
addKey({
hand.transform = .identity
}, animationTime: 0)
currentTime += resetDuration
}
UIView.animateKeyframes(withDuration: duration, delay: 0, options: .calculationModeLinear) {
translate(-)
fadeOut()
reset()
fadeIn()
translate(-)
fadeOut()
reset()
fadeIn()
translate(+)
fadeOut()
reset()
fadeIn()
translate(+)
fadeOut()
} completion: { _ in
PlaygroundPage.current.finishExecution()
}
I expect every single translate()
animation to run at the same speed, but for some reason the first and last ones are especially slow, despite using .calculationModeLinear
How to make it so that translationDuration
/ duration
is constant time ?
calculationModeLinear
is different from curveLinear
. It sounds like you want the latter. You are using curveEaseInOut
by default so of course the movement slows near the overall start and finish.
Some hanky panky is needed to overcome Swift’s strict typing. Sample code:
let animationOptions: UIView.AnimationOptions = .curveLinear
let keyframeAnimationOptions = UIView.KeyframeAnimationOptions(rawValue: animationOptions.rawValue)
UIView.animateKeyframes(withDuration: duration, delay: 0.1, options: keyframeAnimationOptions) {