Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIViewPropertyAnimator reverse animation

Tags:

ios

uikit

I have a simple pulse animation which runs perfectly

UIView.animate(withDuration: 1, animations: {
    myAnimatableView.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
}) { _ in
    UIView.animate(withDuration: 1, animations: {
        myAnimatableView.transform = .identity
    })
}

I want to make the same animation, but now with UIViewPropertyAnimator. Is it possible to replicate this pulse effect elegantly with the new API?

I'm doing it this way

let animator = UIViewPropertyAnimator(duration: 1, curve: .linear) {
    animatableView.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
}
animator.addCompletion { _ in
    animator.isReversed = true
    animator.startAnimation()
}
animator.startAnimation()

but my animatableView is just stuck scaled to 1.2.

like image 332
swasta Avatar asked Nov 26 '17 13:11

swasta


2 Answers

It seems that the property animator removes animations after completion (which would explain that starting it again would have no effect) - see SO question. Based on that, You might try hacking it through pausesOnCompletion, but I would expect that that would not be as easy as you might think. Simply because the documentation says:

When the value of this property is true, the animator remains in the active state when the animation finishes, and it does not execute its completion handler.

That means that completion handler won't get called, if the pauseOnCompletion is set to true. According to the same documentation, you can determine when the animator ended (and thus when you should restart the animation) by observing isRunning property of the animator. However, when I tried to implement that, I found out that KVO on isRunning does not get called during the lifecycle of the animator. Thus my recommendation in the end is to simply use two animators, because it seems much easier to implement:

let animator = UIViewPropertyAnimator(duration: 1, curve: .linear) {
    animatableView.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
}
animator.addCompletion { _ in
    let secondAnimator = UIViewPropertyAnimator(duration: 1, curve: .linear) {
        animatableView.transform = CGAffineTransform.identity
    }
    secondAnimator.startAnimation()
}
animator.startAnimation()
like image 156
Milan Nosáľ Avatar answered Oct 24 '22 09:10

Milan Nosáľ


I think your code is more appropiated translated like this

Try with this code

    let animator = UIViewPropertyAnimator(duration: 1, curve: .linear) {
        self.animatableView.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
    }
    animator.addAnimations({
        self.animatableView.transform = CGAffineTransform.identity
    }, delayFactor: 0.5)
    animator.startAnimation()

Or you can try with UILayer animations

Try using UILayer animations

let layerAnimation = CABasicAnimation(keyPath: "transform.scale")
layerAnimation.fromValue = 1
layerAnimation.toValue = 1.2
layerAnimation.isAdditive = false
layerAnimation.duration = CFTimeInterval(1)
layerAnimation.fillMode = kCAFillModeForwards
layerAnimation.isRemovedOnCompletion = true
layerAnimation.repeatCount = .infinity
layerAnimation.autoreverses = true

self.animatableView.layer.add(layerAnimation, forKey: "growingAnimation")
like image 34
Reinier Melian Avatar answered Oct 24 '22 08:10

Reinier Melian