Image Rotate in image view 360 degree in 3D in iOS - ios

I am developing one application, in which i want a UIImage should rotate in a UIImageview 360 degree, in 3 dimensions.
I have tried many codes but all the code with CAAnimation rotates whole image clockwise or flip the whole image view.
So, how can i develop this type of functionality?

In Swift, you can use the following code for infinite rotation:
Swift -
let kRotationAnimationKey = "com.myapplication.rotationanimationkey" // Any key
func rotateView(view: UIView, duration: Double = 1) {
if view.layer.animationForKey(kRotationAnimationKey) == nil {
let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotationAnimation.fromValue = 0.0
rotationAnimation.toValue = Float(M_PI * 2.0)
rotationAnimation.duration = duration
rotationAnimation.repeatCount = Float.infinity
view.layer.addAnimation(rotationAnimation, forKey: kRotationAnimationKey)
}
}
Stopping is like:
func stopRotatingView(view: UIView) {
if view.layer.animationForKey(kRotationAnimationKey) != nil {
view.layer.removeAnimationForKey(kRotationAnimationKey)
}
}
Objective C
this worked perfectly for me: iphone UIImageView rotation
#import <QuartzCore/QuartzCore.h>
- (void) runSpinAnimationOnView:(UIView*)view duration:(CGFloat)duration rotations:(CGFloat)rotations repeat:(float)repeat;
{
CABasicAnimation* rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
rotationAnimation.toValue = [NSNumber numberWithFloat: M_PI * 2.0 /* full rotation*/ * rotations * duration ];
rotationAnimation.duration = duration;
rotationAnimation.cumulative = YES;
rotationAnimation.repeatCount = repeat;
[view.layer addAnimation:rotationAnimation forKey:#"rotationAnimation"];
}

Related

CABasicAnimation flicker when applying the completion

I am trying to apply a rotation animation by number of degrees to a UIImageView and persist the rotation transformation in the completion block.
The problem that I am facing is that when the completion block is executed there is a visible flicker generated by passing from the end state of the animation to the completion block.
Here is the code that I am currently using:
if (futureAngle == currentAngle) {
return;
}
float rotationAngle;
if (futureAngle < currentAngle) {
rotationAngle = futureAngle - currentAngle;
}else{
rotationAngle = futureAngle - currentAngle;
}
float animationDuration = fabs(rotationAngle) / 100;
rotationAngle = GLKMathDegreesToRadians(rotationAngle);
[CATransaction begin];
CABasicAnimation *rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
rotationAnimation.byValue = [NSNumber numberWithFloat:rotationAngle];
rotationAnimation.duration = animationDuration;
rotationAnimation.removedOnCompletion = YES;
[CATransaction setCompletionBlock:^{
view.transform = CGAffineTransformRotate(view.transform, rotationAngle);
}];
[view.layer addAnimation:rotationAnimation forKey:#"rotationAnimation"];
[CATransaction commit];
When you say flicker, I assume you mean that at the end of the animation, that it momentarily returns to the initial state before returning back to the final state? This can be solved either by
setting the final view.transform before you start the animation (and you no longer need the completionBlock);
by setting the animation's fillMode to kCAFillModeForwards and set removedOnCompletion to false.
Personally, I think setting the animated property to its destination value before you start the animation is the easiest way to do this.
Thus:
- (void)rotate:(UIView *)view by:(CGFloat)delta {
float animationDuration = 2.0;
CGFloat currentAngle = self.angle; // retrieve saved angle
CGFloat nextAngle = self.angle + delta; // increment it
self.angle = nextAngle; // save new value
view.transform = CGAffineTransformMakeRotation(nextAngle); // set property to destination rotation
CABasicAnimation *rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"]; // now rotate
rotationAnimation.fromValue = #(currentAngle);
rotationAnimation.toValue = #(nextAngle);
rotationAnimation.duration = animationDuration;
rotationAnimation.removedOnCompletion = YES;
[view.layer addAnimation:rotationAnimation forKey:#"rotationAnimation"];
}
Or, I think even easier, just adjust the transform:
- (void)rotate:(UIView *)view by:(CGFloat)delta {
float animationDuration = 2.0;
CGAffineTransform transform = view.transform; // retrieve current transform
CGAffineTransform nextTransform = CGAffineTransformRotate(transform, delta); // increment it
view.transform = nextTransform; // set property to destination rotation
CABasicAnimation *rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform"]; // now rotate
rotationAnimation.fromValue = [NSValue valueWithCGAffineTransform:transform];
rotationAnimation.toValue = [NSValue valueWithCGAffineTransform:nextTransform];
rotationAnimation.duration = animationDuration;
rotationAnimation.removedOnCompletion = YES;
[view.layer addAnimation:rotationAnimation forKey:#"rotationAnimation"];
}
I was seeing flickering even when using the suggested answer from Rob, but turns out it seems to just be a simulator bug. On real devices I dont see the flicker, if you have only been testing on simulator, try on a real device unless you want to waste hours of your life potentially like myself.

swift rotating view pause

I want to make infinite rotation. But I see a rotating pause. Can you explain why?
UIView.animateWithDuration(0.5, delay: 0, options: UIViewAnimationOptions.Repeat | UIViewAnimationOptions.CurveLinear, animations:
{
self.myview.transform = CGAffineTransformRotate(self.myview.transform, CGFloat(M_PI_2))
}, completion: { nil
}()
)
I think you want circular rotation of UIView. you can achieved using below code.
CABasicAnimation* rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
rotationAnimation.toValue = [NSNumber numberWithFloat: M_PI * 2.0 * 1 * 3 ];
rotationAnimation.duration = 3;
rotationAnimation.cumulative = YES;
rotationAnimation.repeatCount = 5;
[btnPush.layer addAnimation:rotationAnimation forKey:#"rotationAnimation"];

Getting transform.rotation.z value from an animated view

I'm spinning continuously an UIImageView using CABasicAnimation and I want to be able to get the rotation angle.
-(void)viewDidAppear:(BOOL)animated{
[self spinAnimationOnView:self.myImageView
duration:2
repeat:HUGE_VAL];
}
- (void)spinAnimationOnView:(UIView*)view duration:(CGFloat)duration repeat:(float)repeat{
CABasicAnimation* rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
rotationAnimation.toValue = [NSNumber numberWithFloat: M_PI * 2.0];
rotationAnimation.duration = duration;
rotationAnimation.repeatCount = repeat;
[view.layer addAnimation:rotationAnimation forKey:#"rotationAnimation"];
}
The animation behaves as expected. But then I try to get the rotation angle like this:
CGFloat angle = [(NSNumber *)[self.myImageView valueForKeyPath:#"layer.transform.rotation.z"] floatValue];
NSLog(#"ROTATION = %f", angle);
And the output is always 0. Any thoughts?
While an animation is in progress, you need to use the CALayer's presentationLayer to get the state of the layer. See the CALayer docs.
CGFloat angle = [[self.myImageView valueForKeyPath:#"layer.presentationLayer.transform.rotation.z"] floatValue];
NSLog(#"ROTATION = %f", angle);

ios how to rotate control 360 degree

I want to rotate control over 360 degrees. I do it like that right now
#define degreesToRadians(x) (M_PI * x / 180.0)
[UIView animateWithDuration:0.25 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
[self.crossButton setTransform:CGAffineTransformRotate(self.crossButton.transform, degreesToRadians(360))];
} completion:nil];
But nothings happens. Do you know what I'm doing wrong?
A rotation transformation by 360 degrees is the identity transformation, therefore the
object is not animated at all.
You can achieve the desired effect with a "basic property animation":
[CATransaction begin];
CABasicAnimation *rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
rotationAnimation.byValue = #(2 * M_PI); // 360 degrees
rotationAnimation.duration = 0.25;
rotationAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
[self.crossButton.layer addAnimation:rotationAnimation forKey:#"rotationAnimation"];
[CATransaction commit];
Update: If the rotation amount is not 360 degrees (so that the final position is not the same
as the initial position) then the following should work:
[CATransaction begin];
CGFloat angle = 45.0 * M_PI/180.0;
CABasicAnimation *rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
rotationAnimation.byValue = #(angle);
rotationAnimation.duration = 0.25;
rotationAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
rotationAnimation.removedOnCompletion = YES;
[CATransaction setCompletionBlock:^{
self.crossButton.transform = CGAffineTransformRotate(self.button.transform, angle);
}];
[self.crossButton.layer addAnimation:rotationAnimation forKey:#"rotationAnimation"];
[CATransaction commit];
If you are rotating for 360 degree, you are putting the object back into same position. If you want to see animation, increase the delay and check. You will find entire object to move for a perfect circle.
Try below code.
[UIView animateWithDuration:2.0 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
self.crossButton.transform = CGAffineTransformMakeRotation(M_PI);
} completion:nil];
Swift 3:
Using CABasicAnimation
var rotationAnimation = CABasicAnimation()
rotationAnimation = CABasicAnimation.init(keyPath: "transform.rotation.z")
rotationAnimation.toValue = NSNumber(value: (Double.pi * 2.0))
rotationAnimation.duration = 1.0
rotationAnimation.isCumulative = true
rotationAnimation.repeatCount = 100.0
view.layer.add(rotationAnimation, forKey: "rotationAnimation")
Here is an extension functions for UIView that handles start & stop rotation operations:
extension UIView {
func startRotation() {
let rotation : CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
rotation.fromValue = 0
rotation.toValue = NSNumber(value: Double.pi * 2.0)
rotation.duration = 1.0
rotation.isCumulative = true
rotation.repeatCount = FLT_MAX
self.layer.add(rotation, forKey: "rotationAnimation")
}
func stopRotation() {
self.layer.removeAnimation(forKey: "rotationAnimation")
}
}
Now using, UIView.animation closure:
UIView.animate(withDuration: 0.5, animations: {
button.transform = CGAffineTransform(rotationAngle: (CGFloat(Double.pi))
}) { (isHalfAnimationComplete) in
UIView.animate(withDuration: 0.5) {
button.transform = CGAffineTransform(rotationAngle: (CGFloat(Double.pi * 2))
}
}

How do you create a wiggle animation similar to iphone deletion animation

We are currently developing an application that contains a series of icons. We want the icons to wiggle like the app deletion animations when pressed. What would be the best way to code this animation sequence?
The answer by Vinzius is very cool. However the wobble only rotates from 0 Radians to 0.08. Thus the wobble can look a little unbalanced. If you get this same issue then you may want to add both a negative and a positive rotation by using a CAKeyframeAnimation rather than a CABasicRotation:
- (CAAnimation*)getShakeAnimation
{
CAKeyframeAnimation* animation = [CAKeyframeAnimation animationWithKeyPath:#"transform"];
CGFloat wobbleAngle = 0.06f;
NSValue* valLeft = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(wobbleAngle, 0.0f, 0.0f, 1.0f)];
NSValue* valRight = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(-wobbleAngle, 0.0f, 0.0f, 1.0f)];
animation.values = [NSArray arrayWithObjects:valLeft, valRight, nil];
animation.autoreverses = YES;
animation.duration = 0.125;
animation.repeatCount = HUGE_VALF;
return animation;
}
You can use this animation method for your view or button like this.
[self.yourbutton.layer addAnimation:[self getShakeAnimation] forKey:#""];
SWIFT :-
let transformAnim = CAKeyframeAnimation(keyPath:"transform")
transformAnim.values = [NSValue(CATransform3D: CATransform3DMakeRotation(0.04, 0.0, 0.0, 1.0)),NSValue(CATransform3D: CATransform3DMakeRotation(-0.04 , 0, 0, 1))]
transformAnim.autoreverses = true
transformAnim.duration = (Double(indexPath.row)%2) == 0 ? 0.115 : 0.105
transformAnim.repeatCount = Float.infinity
self.layer.addAnimation(transformAnim, forKey: "transform")
Objective C :-
-(CAKeyframeAnimation *)wiggleView
{
CAKeyframeAnimation* animation = [CAKeyframeAnimation animationWithKeyPath:#"transform"];
CGFloat wobbleAngle = 0.04f;
NSValue* valLeft = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(wobbleAngle, 0.0f, 0.0f, 1.0f)];
NSValue* valRight = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(-wobbleAngle, 0.0f, 0.0f, 1.0f)];
animation.values = [NSArray arrayWithObjects:valLeft, valRight, nil];
animation.autoreverses = YES;
animation.duration = 0.125;
animation.repeatCount = HUGE_VALF;
return animation;
}
Looking at the iOS implementation a bit closer, there are two things that make theirs a bit more realistic than the code mentioned here:
The icons appear to have a bounce as well as a rotation
Every icon has its own timing -- they are not all synchronized
I based myself on the answers here (and with some help from this answer) to add the rotation, the bounce and a bit of randomness to the duration of each animation.
#define kWiggleBounceY 4.0f
#define kWiggleBounceDuration 0.12
#define kWiggleBounceDurationVariance 0.025
#define kWiggleRotateAngle 0.06f
#define kWiggleRotateDuration 0.1
#define kWiggleRotateDurationVariance 0.025
-(void)startWiggling {
[UIView animateWithDuration:0
animations:^{
[self.layer addAnimation:[self rotationAnimation] forKey:#"rotation"];
[self.layer addAnimation:[self bounceAnimation] forKey:#"bounce"];
self.transform = CGAffineTransformIdentity;
}];
}
-(CAAnimation*)rotationAnimation {
CAKeyframeAnimation* animation = [CAKeyframeAnimation animationWithKeyPath:#"transform.rotation.z"];
animation.values = #[#(-kWiggleRotateAngle), #(kWiggleRotateAngle)];
animation.autoreverses = YES;
animation.duration = [self randomizeInterval:kWiggleRotateDuration
withVariance:kWiggleRotateDurationVariance];
animation.repeatCount = HUGE_VALF;
return animation;
}
-(CAAnimation*)bounceAnimation {
CAKeyframeAnimation* animation = [CAKeyframeAnimation animationWithKeyPath:#"transform.translation.y"];
animation.values = #[#(kWiggleBounceY), #(0.0)];
animation.autoreverses = YES;
animation.duration = [self randomizeInterval:kWiggleBounceDuration
withVariance:kWiggleBounceDurationVariance];
animation.repeatCount = HUGE_VALF;
return animation;
}
-(NSTimeInterval)randomizeInterval:(NSTimeInterval)interval withVariance:(double)variance {
double random = (arc4random_uniform(1000) - 500.0) / 500.0;
return interval + variance * random;
}
I implemented this code on a UICollectionView which had 30 items bouncing and the performance was flawless on an iPad 2.
I tried to do something like that for an iPad app.
I tried to do some rotations (with CAAnimation) to the view. Here is a sample code I wrote :
- (CAAnimation*)getShakeAnimation {
CABasicAnimation *animation;
CATransform3D transform;
// Create the rotation matrix
transform = CATransform3DMakeRotation(0.08, 0, 0, 1.0);
// Create a basic animation to animate the layer's transform
animation = [CABasicAnimation animationWithKeyPath:#"transform"];
// Assign the transform as the animation's value
animation.toValue = [NSValue valueWithCATransform3D:transform];
animation.autoreverses = YES;
animation.duration = 0.1;
animation.repeatCount = HUGE_VALF;
return animation;
}
And you should try to apply this one to your layer (with function : addAnimation).
Here, autoreverses property is to alternate left and right orientation.
Try setting others values to the angle and duration.
But in my case I had to add others angles to the CATransform3DMakeRotation method, depending on the initial layer orientation ^^
Good Luck !
Vincent
Rewrote Sebastien's answer in Swift 3.
let wiggleBounceY = 4.0
let wiggleBounceDuration = 0.12
let wiggleBounceDurationVariance = 0.025
let wiggleRotateAngle = 0.06
let wiggleRotateDuration = 0.10
let wiggleRotateDurationVariance = 0.025
func randomize(interval: TimeInterval, withVariance variance: Double) -> Double{
let random = (Double(arc4random_uniform(1000)) - 500.0) / 500.0
return interval + variance * random
}
func startWiggle(for view: UIView){
//Create rotation animation
let rotationAnim = CAKeyframeAnimation(keyPath: "transform.rotation.z")
rotationAnim.values = [-wiggleRotateAngle, wiggleRotateAngle]
rotationAnim.autoreverses = true
rotationAnim.duration = randomize(interval: wiggleRotateDuration, withVariance: wiggleRotateDurationVariance)
rotationAnim.repeatCount = HUGE
//Create bounce animation
let bounceAnimation = CAKeyframeAnimation(keyPath: "transform.translation.y")
bounceAnimation.values = [wiggleBounceY, 0]
bounceAnimation.autoreverses = true
bounceAnimation.duration = randomize(interval: wiggleBounceDuration, withVariance: wiggleBounceDurationVariance)
bounceAnimation.repeatCount = HUGE
//Apply animations to view
UIView.animate(withDuration: 0) {
view.layer.add(rotationAnim, forKey: "rotation")
view.layer.add(bounceAnimation, forKey: "bounce")
view.transform = .identity
}
}
func stopWiggle(for view: UIView){
view.layer.removeAllAnimations()
}
func startWiggling() {
deleteButton.isHidden = false
guard contentView.layer.animation(forKey: "wiggle") == nil else { return }
guard contentView.layer.animation(forKey: "bounce") == nil else { return }
let angle = 0.04
let wiggle = CAKeyframeAnimation(keyPath: "transform.rotation.z")
wiggle.values = [-angle, angle]
wiggle.autoreverses = true
wiggle.duration = randomInterval(0.1, variance: 0.025)
wiggle.repeatCount = Float.infinity
contentView.layer.add(wiggle, forKey: "wiggle")
let bounce = CAKeyframeAnimation(keyPath: "transform.translation.y")
bounce.values = [4.0, 0.0]
bounce.autoreverses = true
bounce.duration = randomInterval(0.12, variance: 0.025)
bounce.repeatCount = Float.infinity
contentView.layer.add(bounce, forKey: "bounce")
}
func stopWiggling() {
deleteButton.isHidden = true
contentView.layer.removeAllAnimations()
}
func randomInterval(_ interval: TimeInterval, variance: Double) -> TimeInterval {
return interval + variance * Double((Double(arc4random_uniform(1000)) - 500.0) / 500.0)
}
Look at this iOS SpingBoard example
Answered in another thread a Swift 4 version of what is apparently Apple's own algorithm reverse engineered:
https://stackoverflow.com/a/47730519/5018607
Edited paiego's code to fit my needs: visual error animation feedback upon user's action (tap). It happens once - it's not a constant wiggling like SpringBoard app edit wiggle animation.
- (CAAnimation *)shakeAnimation {
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:#"transform"];
CGFloat wobbleAngle = 0.06f;
NSValue *valLeft;
NSValue *valRight;
NSMutableArray *values = [NSMutableArray new];
for (int i = 0; i < 5; i++) {
valLeft = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(wobbleAngle, 0.0f, 0.0f, 1.0f)];
valRight = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(-wobbleAngle, 0.0f, 0.0f, 1.0f)];
[values addObjectsFromArray:#[valLeft, valRight]];
wobbleAngle*=0.66;
}
animation.values = [values copy];
animation.duration = 0.7;
return animation;
}
Usage:
[your_view.layer addAnimation:[self shakeAnimation] forKey:#""]; //do the shake animation
your_view.transform = CGAffineTransformIdentity; //return the view back to original
Hope this helps someone else.

Resources