IOS Need to rotate a Image and keep it there, with animation - ios

im looking to Rotate a UIImageView, and i found some helpfull code
- (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"];
}
The issue is that the image flips back when the animation finishes, im looking to rotate it and keep it there. Would i need to flip the actual image or can i flip the ImageView. And how would i do that`?

Why not use a standard UIView animation?
If you really need the repeatCount, then I suggest you use the old style:
[UIView beginAnimations]
[UIView setAnimationRepeatCount:repeatCount];
[UIView setAnimationDuration:duration];
yourView.transform = CGAffineTransformMakeRotation(degrees * M_PI);
[UIView commitAnimations];
Otherwise, you can use the much preferred new style:
[UIView animateWithDuration:duration animations:^{
yourView.transform = CGAffineTransformMakeRotation(angle);
} completion:^(BOOL finished) {
// your completion code here
}]
If you need other options like autoreverse, allow user interaction or repeating use [UIView animateWithDuration:delay:options:animations:completion:];

I prefer to cover all my bases with CoreAnimation. Set the final value before the animation, set the fill mode, and set do not remove.
- (void) runSpinAnimationOnView:(UIView*)view duration:(CGFloat)duration rotations: (CGFloat)rotations repeat:(float)repeat;
{
NSString * animationKeyPath = #"transform.rotation.z";
CGFloat finalRotation = M_PI * 2.0f * rotations;
[view.layer setValue:#(finalRotation) forKey:animationKeyPath];
CABasicAnimation* rotationAnimation = [CABasicAnimation animationWithKeyPath:animationKeyPath];
rotationAnimation.fromValue = #0.0f;
rotationAnimation.toValue = #(finalRotation);
rotationAnimation.fillMode = kCAFillModeBoth;
rotationAnimation.removedOnCompletion = NO;
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.

Animation does not work after the first time to return to the original state

I have two UIImages one over another, with the same size (40 by 40) it's a simple dot on the whole UIImage and I want to animate rotating as a 3D. When the first UIImage "colapse" and it is not visible, change the property HIDDEN to YES, and immediately the UIImage under the first begin to appears, and when the user press the button again everything turn back at the first state. But i works only the first time, after that, just appears and disappears the images.
Heres is my code:
#interface ViewController ()
{
BOOL myFlag;
}
#end
- (void)viewDidLoad {
[super viewDidLoad];
self.C002.image = [Test imageOfCircle002];
self.C005.image = [Test imageOfCircle005];
self.C005.hidden = YES;
myFlag = YES;
}
- (IBAction)animationButton:(id)sender
{
if (myFlag)
{
//First state of the UIImages,
[UIView animateWithDuration:1.0 animations:^{
//3D rotation C002
CABasicAnimation* animation;
animation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.y"];
animation.fromValue = #(0);
animation.toValue = #(0.5 * M_PI);
animation.repeatCount = 1;
animation.duration = 1.0;
[self.C002.layer addAnimation:animation forKey:#"rotation"];
CATransform3D transform = CATransform3DIdentity;
transform.m34 = 1.0 / 500.0;
self.C002.layer.transform = transform;
} completion:^(BOOL finished) {
self.C002.hidden = YES; //Hide the first image
self.C005.hidden = NO; //Show the second and animate
[UIView animateWithDuration:1.0 animations:^{
//3D rotation C005
CABasicAnimation* animation2 = [CABasicAnimation animationWithKeyPath:#"transform.rotation.y"];
animation2.fromValue = #(0.5 * M_PI); //begin where the first UIImage ends
animation2.toValue = #(1.0 * M_PI);
animation2.repeatCount = 1;
animation2.duration = 1.0;
[self.C005.layer addAnimation:animation2 forKey:#"rotation"];
CATransform3D transform2 = CATransform3DIdentity;
transform2.m34 = 1.0 / 500.0;
self.C005.layer.transform = transform2;
} completion:^(BOOL finished) {
[self.C002.layer removeAnimationForKey:#"rotation"];
[self.C005.layer removeAnimationForKey:#"rotation"];
}];
}];
myFlag = NO; // Set the flag to other state.
}
else
{ //Heres try to turn everything back, but... not working.
[UIView animateWithDuration:1.0 animations:^{
//3D rotation C005
CABasicAnimation* animation;
animation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.y"];
animation.fromValue = #(1.0 * M_PI); //begin where is in the last animation
animation.toValue = #(0.5 * M_PI);
animation.repeatCount = 1;
animation.duration = 1.0;
[self.C005.layer addAnimation:animation forKey:#"rotation"];
CATransform3D transform = CATransform3DIdentity;
transform.m34 = 1.0 / 500.0;
self.C005.layer.transform = transform;
} completion:^(BOOL finished) {
self.C005.hidden = YES;
self.C002.hidden = NO;
[UIView animateWithDuration:1.0 animations:^{
//3D rotation C002
CABasicAnimation* animation2 = [CABasicAnimation animationWithKeyPath:#"transform.rotation.y"];
animation2.fromValue = #(0.5 * M_PI);
animation2.toValue = #(0 * M_PI);
animation2.repeatCount = 1;
animation2.duration = 1.0;
[self.C002.layer addAnimation:animation2 forKey:#"rotation"];
CATransform3D transform2 = CATransform3DIdentity;
transform2.m34 = 1.0 / 500.0;
self.C002.layer.transform = transform2;
} completion:^(BOOL finished) {
[self.C002.layer removeAnimationForKey:#"rotation"];
[self.C005.layer removeAnimationForKey:#"rotation"];
}];
}];
myFlag = YES;
}
}
Hope guys, you can help me.

Converting code from CABasicAnimation

I have a animation code that is written using CABasicAnimation just as following:
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"position"];
[anim setDelegate:self];
anim.fromValue = [NSValue valueWithCGPoint:img.center];
if (newValue == 0) {
imgCentre.y = centerStart.y - 11 * kCounterDigitDiff;
anim.toValue = [NSValue valueWithCGPoint:imgCentre];
} else
anim.toValue = [NSValue valueWithCGPoint:imgCentre];
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
anim.duration = 0.3;
[img.layer addAnimation:anim forKey:#"rollLeft"];
img.frame = frame;
Due to some reason I wish to apply same animation but by using following type of approach:
[UIView animateWithDuration:0.3 delay:0.0 options:(UIViewAnimationOptionBeginFromCurrentState|UIViewAnimationOptionCurveEaseOut) animations:^{
...
} completion:^(BOOL finished) {
...
}
What I come up with is like following:
[img setCenter:imgCentre];
[UIView animateWithDuration:0.3 delay:0.0 options:(UIViewAnimationOptionBeginFromCurrentState|UIViewAnimationOptionCurveEaseOut) animations:^{
if (newValue == 0) {
CGPoint newPoint= CGPointMake(imgCentre.x, centerStart.y - 11 * kCounterDigitDiff);
[img setCenter:newPoint];
} else{
[img setCenter:imgCentre];
}
} completion:^(BOOL finished) {
// animation completion code
}];
img.frame = frame;
Overall things look good but I am a little bit pessimistic about the conversion? is there something that i have missed?
You've got the right idea. However, unlike with CAAnimations, you don't have to set the properties to the end value. The UIView animation call takes care of all that for you.
UIView animation is generally easier and more intuitive to use than CAAnimation.

How to rotate the image from original and back to it?

I have try to rotate the image by using CGAffineTransformMakeRotation and CGAffineTransformRotate. I want to rotate the image from it's original and clockwise to the original again(like degree 0 to 360). I need to rotate image like a CD on the player. I did something like this:
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.1];
CGAffineTransform transform = CGAffineTransformMakeRotation(1.0);
transform = CGAffineTransformRotate(transform, -180);
ImageView.transform = transform;
[UIView commitAnimations];
it just spin to the degree 180, and if I change it to -300 it will rotate counterclockwise (that not what I need).
the angle of rotation is given better by atan2() suppose A and B are two points on the centerline to which your image is to be rotated then the angle of rotation is
0-atan2((b.x - a.x) ,(b.y -a.y))
Hope this helps
Try this:
-(void)startAnimationWithRevolutions:(float)revPerSecond forTime:(float)time
{
spinWheel.userInteractionEnabled = FALSE;
float totalRevolutions = revPerSecond * time;
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithFloat:time] forKey:kCATransactionAnimationDuration];
CABasicAnimation* spinAnimation = [CABasicAnimation
animationWithKeyPath:#"transform.rotation"];
CGAffineTransform transform = spinWheel.transform;
float fromAngle = atan2(transform.b, transform.a);
float toAngle = fromAngle + (totalRevolutions*4*M_PI);
spinAnimation.fromValue = [NSNumber numberWithFloat:fromAngle];
spinAnimation.toValue = [NSNumber numberWithFloat:toAngle];
spinAnimation.repeatCount = 0;
spinAnimation.removedOnCompletion = NO;
spinAnimation.delegate = self;
spinAnimation.timingFunction = [CAMediaTimingFunction functionWithName:
kCAMediaTimingFunctionEaseOut];
[spinWheel.layer addAnimation:spinAnimation forKey:#"spinAnimation"];
[CATransaction commit];
}
Use this:
[UIView animateWithDuration:0.1 animations:^{
CGAffineTransform transform = CGAffineTransformMakeRotation(1.0);
transform = CGAffineTransformRotate(transform, -180);
ImageView.transform = transform;
} completion:^(BOOL finished) {
CGAffineTransform transform = CGAffineTransformMakeRotation(0);
transform = CGAffineTransformRotate(transform,0);
ImageView.transform = transform;
}];

Multiple CALayer animations Executed at the Same time

I am trying to make an animation where two layers move to the side, scale down, and rotate a little bit in 3D, all at the same time (then move back with the layer previously at the bottom not on top). I tried several methods, but none seem to work.
I have the 3d transform animation like so:
perspectiveTransformLeft = CATransform3DIdentity;
perspectiveTransformLeft.m34 = 1.0 / 500;
perspectiveTransformLeft = CATransform3DRotate(perspectiveTransformLeft, 35.0f * M_PI / 360.0f, 0.0f, 1.0f, 0.0f);
I've tried adding a scale transform that didn't work:
perspectiveTransformLeft = CATransform3DMakeScale(0.75, 0.75, 1);
I've tried to scale the layer in an animation block, but that didn't work either:
[UIView animateWithDuration:1.0f
delay:0.0f
options: UIViewAnimationOptionCurveEaseInOut
animations:^{
endingLayer.frame = CGRectMake(20.0f, 0.0f, 724.0f, 538.0f);
switchViewBottom.layer.transform = perspectiveTransformRight;
}
completion:^(BOOL finished){
[delegate switchAnimationFinished];
}
];
I am at a loss. Can someone help me?
Do some reading on CAAnimationGroup and use CABasicAnimations instead.
That should held you achieve what you're after. I'll search for an example in my code (I previously used it) if you'll have issues implementing it.
Edit: Here's some code
typedef void (^animationCompletionBlock)(void);
typedef void (^animationStartedBlock)(void);
- (void)addAnimations:(NSArray *)animations withDuration:(CGFloat)animationDuration onView:(UIView *)aView {
animationStartedBlock startBlock = ^void(void) {
// Additional Animation start code here
};
animationCompletionBlock endBlock = ^void(void) {
// Additional animation completed code here
};
CAAnimationGroup *group = [CAAnimationGroup animation];
group.fillMode = kCAFillModeForwards;
group.removedOnCompletion = NO;
group.duration = animationDuration;
[group setAnimations:animations];
group.delegate = self;
[group setValue:startBlock forKey:#"animationStartedBlock"];
[group setValue:endBlock forKey:#"animationCompletionBlock"];
[aView.layer addAnimation:group forKey:#"yourAnimationName"];
}
This will have your completion blocks called in your delegate
// Animation Delegate
- (void)animationDidStart:(CAAnimation *)anim {
animationStartedBlock animationStartedBlock = [anim valueForKey:#"animationStartedBlock"];
if (animationStartedBlock) {
animationStartedBlock();
}
}
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag {
animationCompletionBlock animationCompleteBlock = [theAnimation valueForKey:#"animationCompletionBlock"];
if (animationCompleteBlock) {
animationCompleteBlock();
}
}
How you create the animations and add them to an array to pass to this method is up to you, depending on what animations you want.
This is an example for two scale / fade animations:
// Scale
- (CABasicAnimation *)scaleAnimationForImageView:(UIImageView *)imageView withDuration:(CGFloat)duration {
CGRect imageFrame = imageView.frame;
CABasicAnimation *resizeAnimation = [CABasicAnimation animationWithKeyPath:#"bounds.size"];
[resizeAnimation setToValue:[NSValue valueWithCGSize:CGSizeMake(40.0f, imageFrame.size.height * (40.0f / imageFrame.size.width))]];
resizeAnimation.fillMode = kCAFillModeForwards;
resizeAnimation.duration = duration;
resizeAnimation.removedOnCompletion = NO;
return resizeAnimation;
}
// Fade
- (CABasicAnimation *)fadeAnimationWithFinalOpacity:(CGFloat)opacity withDuration:(CGFloat)duration {
CABasicAnimation *fadeOutAnimation = [CABasicAnimation animationWithKeyPath:#"opacity"];
[fadeOutAnimation setToValue:[NSNumber numberWithFloat:opacity]];
fadeOutAnimation.fillMode = kCAFillModeForwards;
fadeOutAnimation.removedOnCompletion = NO;
fadeOutAnimation.duration = duration;
return fadeOutAnimation;
}

Resources