Start/stop image view rotation animations
Swift 3.x
Start Animation
let rotationAnimation : CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
rotationAnimation.toValue = NSNumber(value: .pi * 2.0)
rotationAnimation.duration = 0.5;
rotationAnimation.isCumulative = true;
rotationAnimation.repeatCount = .infinity;
self.imageWood?.layer.add(rotationAnimation, forKey: "rotationAnimation")
Stop animation
self.imageWood?.layer.removeAnimation(forKey: "rotationAnimation")
Swift 2.x
Start Animation
let rotationAnimation : CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
rotationAnimation.toValue = NSNumber(double: M_PI * 2.0)
rotationAnimation.duration = 1;
rotationAnimation.cumulative = true;
rotationAnimation.repeatCount = .infinity;
self.imageWood?.layer.addAnimation(rotationAnimation, forKey: "rotationAnimation")
Stop animation
self.imageWood?.layer.removeAnimation(forKey: "rotationAnimation")
Rotate an Imagewith Animation
First of all, what is you minimum SDK requirement? In case it's at least Android 3.0, you can use the newer animation framework, and animate your Image with something like this:
imageView.animate().rotation(180).start();
About the flicker: I wouldn't reset the source image of the ImageView after the rotation, I'd just leave in the original and make sure that the rotation animation fills after the animation, leaving the image rotated. The flicker is most likely caused by the View's relayout/redraw upon changing the source image.
Further visual artifacts (flicker?) may be caused because the original-rotated image and the rotated static image might differ in a few pixels.
How to make a smooth image rotation in Android?
You are right about AccelerateInterpolator; you should use LinearInterpolator instead.
You can use the built-in android.R.anim.linear_interpolator
from your animation XML file with android:interpolator="@android:anim/linear_interpolator"
.
Or you can create your own XML interpolation file in your project, e.g. name it res/anim/linear_interpolator.xml
:
<?xml version="1.0" encoding="utf-8"?>
<linearInterpolator xmlns:android="http://schemas.android.com/apk/res/android" />
And add to your animation XML:
android:interpolator="@anim/linear_interpolator"
Special Note: If your rotate animation is inside a set, setting the interpolator does not seem to work. Making the rotate the top element fixes it. (this will save your time.)
Android: ImageView Rotate Animation working only once
I am pretty sure that the rotation is kept at the end of the animation. So the second time you try to animate the image it doesn't move because it already is rotated to 1800.
So either reset your image to 0 or rotate it to currentRotation + 1800f
.
Proper way to stop an infinitely rotating image? and how does one implement removeAllAnimations?
There are a couple of ways to do what you're asking about:
If supporting iOS 10+, you can use
UIViewPropertyAnimator
, whose animations you can pause and restart (resuming from where it was paused):private var animator: UIViewPropertyAnimator?
deinit {
animator?.stopAnimation(true)
}
@IBAction func didTapButton(_ sender: Any) {
guard let animator = animator else {
createAnimation()
return
}
if animator.isRunning {
animator.pauseAnimation()
} else {
animator.startAnimation()
}
}
/// Create and start 360 degree animation
///
/// This will fire off another animation when one 360° rotation finishes.
private func createAnimation() {
animator = UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 4, delay: 0, options: .curveLinear) { [self] in
UIView.animateKeyframes(withDuration: 4, delay: 0) {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 1.0 / 3.0) {
animatedView.transform = .init(rotationAngle: .pi * 2 * 1 / 3)
}
UIView.addKeyframe(withRelativeStartTime: 1.0 / 3.0, relativeDuration: 1.0 / 3.0) {
animatedView.transform = .init(rotationAngle: .pi * 2 * 2 / 3)
}
UIView.addKeyframe(withRelativeStartTime: 2.0 / 3.0, relativeDuration: 1.0 / 3.0) {
animatedView.transform = .identity
}
}
} completion: { [weak self] _ in
self?.createAnimation()
}
}You can alternatively use UIKit Dynamics to rotate the item. You can then remove a
UIDynamicItemBehavior
that was performing the rotation and it just stops where it was. It automatically leaves the viewtransform
where it was. Then, to resume the rotation, just add aUIDynamicItemBehavior
for the rotation again:private lazy var animator = UIDynamicAnimator(referenceView: view)
private var rotate: UIDynamicItemBehavior!
@IBAction func didTapButton(_ sender: Any) {
if let rotate = rotate {
animator.removeBehavior(rotate)
self.rotate = nil
} else {
rotate = UIDynamicItemBehavior(items: [animatedView])
rotate.allowsRotation = true
rotate.angularResistance = 0
rotate.addAngularVelocity(1, for: animatedView)
animator.addBehavior(rotate)
}
}This doesn't let you easily control the speed of the rotation in terms of time, but rather it’s dictated by
angularVelocity
, but it's a nice simple approach (and supports iOS 7.0 and later).The old-school approach for stopping an animation and leaving it where you stopped it is to capture the
presentationLayer
of the animation (which shows where it was mid-flight). Then you can grab the current state, stop the animation, and set the transform to what thepresentationLayer
reported.private var isAnimating = false
@IBAction func didTapButton(_ sender: Any) {
if isAnimating {
let transform = animatedView.layer.presentation()!.transform
animatedView.layer.removeAllAnimations()
animatedView.layer.transform = transform
} else {
let rotate = CABasicAnimation(keyPath: "transform.rotation")
rotate.byValue = 2 * CGFloat.pi
rotate.duration = 4
rotate.repeatCount = .greatestFiniteMagnitude
animatedView.layer.add(rotate, forKey: nil)
}
isAnimating = !isAnimating
}If you want to use
UIView
block based animation, you have to capture the angle at which you stopped the animation, so you know from where to restart the animation. The trick is grabm12
andm11
of theCATransform3D
:angle = atan2(transform.m12, transform.m11)
Thus, this yields:
private var angle: CGFloat = 0
private var isAnimating = false
@IBAction func didTapButton(_ sender: Any) {
if isAnimating {
let transform = animatedView.layer.presentation()!.transform
angle = atan2(transform.m12, transform.m11)
animatedView.layer.removeAllAnimations()
animatedView.layer.transform = transform
} else {
UIView.animate(withDuration: 4, delay: 0, options: .curveLinear) { [self] in
UIView.animateKeyframes(withDuration: 4, delay: 0, options: .repeat) {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 1.0 / 3.0) {
animatedView.transform = .init(rotationAngle: self.angle + .pi * 2 * 1 / 3)
}
UIView.addKeyframe(withRelativeStartTime: 1.0 / 3.0, relativeDuration: 1.0 / 3.0) {
animatedView.transform = .init(rotationAngle: self.angle + .pi * 2 * 2 / 3)
}
UIView.addKeyframe(withRelativeStartTime: 2.0 / 3.0, relativeDuration: 1.0 / 3.0) {
animatedView.transform = .init(rotationAngle: self.angle)
}
}
}
}
isAnimating = !isAnimating
}You can rotate the object yourself using
CADisplayLink
that updates the angle to some calculated value. Then stopping the rotation is as simple as invalidating the display link, thereby leaving it where it was when it stopped. You can then resume animation by simply adding the display link back to your runloop.This sort of technique gives you a great deal of control, but is the least elegant of the approaches.
Android: How can I stop an infinite animation applied on an ImageView?
You can also call anim.cancel();
but you should also call anim.reset();
immediately after it.
Then when you want to start it again, just call startAnimation
on the view.
Clockwise image rotation animation
Try this
func rotateView(targetView: UIView, duration: Double = 1.0) {
UIView.animate(withDuration: duration, delay: 0.0, options: .curveLinear, animations: {
targetView.transform = targetView.transform.rotated(by: CGFloat(M_PI))
}) { finished in
self.rotateView(targetView: YOUR_LOGO, duration: duration)
}
}
How to use
self.rotateView(targetView: YOUR_LOGO, duration: duration)
Related Topics
Recognizing Touch Events Only Inside the Masked View
Cmpedometer Querypedometerdatafromdate Returns Error 103
Swift Add Line Above to Control
Swiftui Tabbedview Only Shows First Tab's Content
How to Handle Oauth-Style Log Ins with Imessage Apps in iOS 10, Xcode 8
How to Trigger an API Call Before Displaying Local Notifications
How to Use Nsuserdefaults to Store an Array of Custom Classes in Swift
Swift - Parse a String Which Contains a Url
Uitableview Inside Uitableviewcell with Dynamic Height
How to Run My Performance Tests More Than Ten Times
Memory Usage Keeps Rising on Older Devices Using Metal
How Do View Types Like Text or Image Conform to the View Protocol in Swiftui