Mozahler
Mozahler

Reputation: 5303

How do eliminate the "jump" in my CABasicAnimation?

Using this code:

func rotateTheView(_ aView: UIView, inClockwiseDirection isClockwise: Bool) {
    let multiplier = (isClockwise ? 1 : -1)
    let key = (isClockwise ? "Spin" : "Rotate")
    var rotation: CABasicAnimation?
    rotation = CABasicAnimation(keyPath: "transform.rotation")
    rotation!.fromValue = Int(0)
    let multiplicand = multiplier * 2
    rotation!.toValue = Int(Double(multiplicand) * .pi)
    rotation!.duration = 30 // Speed
    rotation!.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
    rotation!.repeatCount = HUGE  //HUGE_VALF Repeat forever.
    aView.layer.add(rotation!, forKey: key)
}

I get the animation I want. (Either a wheel spins, or a cell rotates fast enough in the opposite direction to always remain exactly right side up).

However, when the 30 seconds (duration) is up, there is a flicker as the view jumps back to how it looked before the animation.

I understand it is supposed to work this way.

How do I apply the rotation to the "before" image so that when the duration expires I don't see any cells jump?

Increasing the duration of the animation slows the wheel's spin, so that is not an appropriate solution.

If #22521690 applies, I don't understand how - I do not have an explicit CATransaction.

Upvotes: 2

Views: 571

Answers (2)

Duncan C
Duncan C

Reputation: 131398

On the next line after applying the animation to the layer, set the property you're animating to its ending value.

Because an animation is "in-flight", the normal display of the layer is covered the presentation layer, a special animation layer.

Once the animation is complete, the presentation layer is hidden/removed, and the actual layer is exposed. If it is a the same state as the presentation layer at the end of the animation then there's no jump.

That code might look like this:

func rotateTheView(_ aView: UIView, inClockwiseDirection isClockwise: Bool) {
    let multiplier = (isClockwise ? 1 : -1)
    let key = (isClockwise ? "Spin" : "Rotate")
    var rotation: CABasicAnimation?
    rotation = CABasicAnimation(keyPath: "transform.rotation")
    rotation!.fromValue = 0.0
    let multiplicand = multiplier * 2
    let finalAngle = Double(multiplicand) * .pi
    rotation!.toValue = finalAngle
    rotation!.duration = 30 // Speed
    rotation!.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
    rotation!.repeatCount = HUGE  //HUGE_VALF Repeat forever.
    aView.layer.add(rotation!, forKey: key)

    //-------------------
    //Set the final transform on the layer to the final rotation, 
    //but without animation
    CATransaction.begin()
    CATransaction.setDisableActions(true)
    let finalTransform = CATransform3DMakeRotation(CGFloat(finalAngle), 0.0, 0.0, 1.0)
    aView.layer.transform = finalTransform
    CATransaction.commit()
    //-------------------
}

Upvotes: 2

Jen Jose
Jen Jose

Reputation: 4035

Try

rotation!.toValue = Double(multiplicand) * .pi

instead of

rotation!.toValue = Int(Double(multiplicand) * .pi)

The issue is with the radian precision which is lost due to Int conversion.

Upvotes: 2

Related Questions