CAAnimation layer cancels previos transformation of the view - ios

I am using a simple transformation animation to an UIIMageView.
[UIView animateWithDuration:2 delay:0 options:UIViewAnimationOptionBeginFromCurrentState|UIViewAnimationOptionCurveEaseInOut animations:^{
CGAffineTransform translate = CGAffineTransformMakeTranslation(0, -[self percentFromHeight:20]);
clueImage.transform = translate;
} completion:^(BOOL finished) {
[self.cluWordSelectionView setaWordsObjects:[NSArray arrayWithArray:[self.whiteCard clueWordObjectsForSoundAtIndex:self.index]]];
[self addSubview:cluWordSelectionView];
[clueImage.layer addAnimation:[Animations shakeAnimation] forKey:#"shake"];
}];
As you can see after the completion I am adding an other shake animation to the view:
[clueImage.layer addAnimation:[Animations shakeAnimation] forKey:#"shake"];
Which looks like that:
+(CAAnimation*)shakeAnimation {
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.beginTime = 3;
animation.autoreverses = YES;
animation.duration = 0.125;
animation.repeatCount = HUGE_VALF;
return animation;
}
The problem is that when the shakeAnimation starts the view "jumps" back to it's original position. How can I prevent that and Why this is happening?
Thanks
shani

The new animation should take the current transform (translation) into account:
CATransform3D leftTransform = CATransform3DMakeRotation(wobbleAngle, 0.0f, 0.0f, 1.0f);
leftTransform = CATransform3DConcat(layer.transform, leftTransform);
CATransform3D rightTransform = CATransform3DMakeRotation(-wobbleAngle, 0.0f, 0.0f, 1.0f);
rightTransform = CATransform3DConcat(layer.transform, rightTransform);
NSValue* valLeft = [NSValue valueWithCATransform3D:leftTransform];
NSValue* valRight = [NSValue valueWithCATransform3D:rightTransform];
Where layer is the layer that will be animated.

Related

Circular animation

I want to implement an animation, its like from a point view starts expanding circularly and filling the entire view. I have implemented the following code but it does not fill the view circularly. Any ideas?
- (void)viewDidLoad {
[super viewDidLoad];
myView =[[UIView alloc]init];
myView.frame=CGRectMake(self.view.frame.size.width/2,self.view.frame.size.height/2+100, .1, .1);
myView.backgroundColor=[UIColor redColor];
[self.view addSubview:myView];
[self animate];
}
-(void)animate{
[UIView animateWithDuration:1.0
delay: 0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
[self setRoundedView: myView toDiameter:1000];
}
completion:nil];
}
-(void)setRoundedView:(UIView *)roundedView toDiameter:(float)newSize;
{
CGPoint saveCenter = roundedView.center;
CGRect newFrame = CGRectMake(roundedView.frame.origin.x, roundedView.frame.origin.y, newSize, newSize);
roundedView.frame = newFrame;
roundedView.layer.cornerRadius = newSize / 2.0;
roundedView.center = saveCenter;
}
You can create a CAShape layer with circular Bezier Path and animate the path property.
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath: #"path"];
Check here for more details : Animating CAShapeLayer size change
If you dont want to use CAnimation you can just scale your rounded view like this :
[UIView animateWithDuration:1.0
delay: 0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
myView.transform = CGAffineTransformMakeScale(scaleX,scaleY);
}
completion:nil];
Try this code. May be helpful.
// Set up the shape of the circle
int radius = 100;
CAShapeLayer *circle = [CAShapeLayer layer];
// Make a circular shape
circle.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2.0*radius, 2.0*radius)
cornerRadius:radius].CGPath;
// Center the shape in self.view
circle.position = CGPointMake(CGRectGetMidX(self.view.frame)-radius,
CGRectGetMidY(self.view.frame)-radius);
// Configure the apperence of the circle
circle.fillColor = [UIColor redColor].CGColor;
circle.strokeColor = [UIColor blackColor].CGColor;
circle.lineWidth = 5;
// Add to parent layer
[self.view.layer addSublayer:circle];
// Configure animation
CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
drawAnimation.duration = 10.0; // "animate over 10 seconds or so.."
drawAnimation.repeatCount = 1.0; // Animate only once..
// Animate from no part of the stroke being drawn to the entire stroke being drawn
drawAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
drawAnimation.toValue = [NSNumber numberWithFloat:1.0f];
// Experiment with timing to get the appearence to look the way you want
drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
// Add the animation to the circle
[circle addAnimation:drawAnimation forKey:#"drawCircleAnimation"];

iOS Animate Rotation at an Angle

I'm trying to do a 360 spin of a view but at a 45 degree tilt.
Like this
I can't figure out for to do it.
So far I've managed this
CABasicAnimation* animation = [CABasicAnimation
animationWithKeyPath:#"transform.rotation.y"];
animation.fromValue = #(0);
animation.toValue = #(2 * M_PI);
animation.duration = 1;
[self.layer addAnimation:animation forKey:#"rotation"];
Which spins it on it's y axis, but what I want is for this Y axis to be tilted 45 degree before it's spun.
First, you should see the link below which explains that the differences between "frame" and "bounds".
UIView frame, bounds and center
And now, here is my answer.
/* add the 4 lines below */
self.transform = CGAffineTransformMakeRotation(M_PI_4);
self.layer.bounds = CGRectMake(0.0f, 0.0f, 60.0f, 200.0f);
self.layer.position = CGPointMake(150.0f, 300.0f);
CABasicAnimation* animation = [CABasicAnimation
animationWithKeyPath:#"transform.rotation.y"];
animation.fromValue = #(0);
animation.toValue = #(2 * M_PI);
animation.duration = 1;
[self.layer addAnimation:animation forKey:#"rotation"];
try this;
CABasicAnimation* animation = [CABasicAnimation
animationWithKeyPath:#"transform"];
CATransform3D rtfm = CATransform3DMakeRotation(2 * M_PI , 1.0f, 1.0f, 0.0f);
rtfm.m34 = -0.0015f;
animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
animation.toValue = [NSValue valueWithCATransform3D:rtfm];
animation.duration = 1;
[self.layer addAnimation:animation forKey:#"rotation"];
1- Change the anchor point to the upper right edge
self.someView.layer.anchorPoint = CGPointMake(1.0, 0.5);
2- rotate the view by 45 or 35 degrees
CGFloat radians = (M_PI/180) * (-35);
self.someView.transform = CGAffineTransformRotate(self.someView.transform, radians);
3- rotate or flip your view by X axis
CGFloat radians = (M_PI/180) * (180);
[UIView animateWithDuration:1 animations:^{
self.someView.layer.transform = CATransform3DRotate(self.someView.layer.transform,radians , 1, 0.0, 0.0);
} completion:^(BOOL finished) {
if(finished) {
}
}];
Works like a charm :)

Rotating UIImageView more than 180 degrees using animations on iOS

Im trying to animate a moving meter by rotating it more than 180 degrees, but it wont work. Im using CGAffineTransform to rotate the meter, which uses a net result to make the rotation. This means that i cannot choose CW or CCW rotation when using this function. How can I modified this code to make it rotate 4 radians. Currently the rotation is transformed to a net result, making the rotation minimal.
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.25];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
CGAffineTransform transform = CGAffineTransformMakeRotation(4);
self.meter.transform = transform;
[UIView commitAnimations];
EDIT:
From the answers i managed to get it working by doing this
[UIView animateWithDuration:1.5 animations:^{
CABasicAnimation *fullRotation;
fullRotation = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
fullRotation.fromValue = [NSNumber numberWithFloat:0];
fullRotation.toValue = [NSNumber numberWithFloat:((330*M_PI)/180)];
fullRotation.duration = 2.0f;
fullRotation.repeatCount = 1;
fullRotation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
fullRotation.fillMode = kCAFillModeForwards;
fullRotation.removedOnCompletion = NO;
// add animation in your view
[self.meter.layer addAnimation:fullRotation forKey:#"330"];
[self performSelector:#selector(stopRotate:) withObject:self.meter afterDelay:2.0];
}];
//Add This in header prefix
#define MAXFLOAT 0x1.fffffep+127f
// add For rotation
[UIView animateWithDuration:1.5 animations:^{
CABasicAnimation *fullRotation;
fullRotation = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
fullRotation.fromValue = [NSNumber numberWithFloat:0];
fullRotation.toValue = [NSNumber numberWithFloat:(2*M_PI))];
fullRotation.duration =1.0f;
fullRotation.repeatCount = MAXFLOAT;
// add animation in your view
[yourView.layer addAnimation:fullRotation forKey:#"360"];
[self performSelector:#selector(stopRotate:) withObject:yourView afterDelay:1.0];
}];
And Add this code to stop rotation From view
-(void)stopRotate :(UIView *)yourView
{
[yourView.layer removeAnimationForKey:#"360"];
}
Try this, just change the basic properties to match your requirements:
CATransform3D rotationTransform = CATransform3DMakeRotation(((4) * (180.0 / M_PI)), 0, 0, 1.0);
CABasicAnimation* rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform"];
rotationAnimation.toValue = [NSValue valueWithCATransform3D:rotationTransform];
rotationAnimation.duration = 0.6f;
rotationAnimation.cumulative = YES;
rotationAnimation.repeatCount = 1;
//Add this two lines
rotationAnimation.fillMode = kCAFillModeForwards;
rotationAnimation.removedOnCompletion = NO;
[self.meter.layer addAnimation:rotationAnimation forKey:#"rotationAnimation"];
Let me know is it works for you.
You need to convert the radian to degree using the following macro:
#define RADIANS_TO_DEGREES(radians) ((radians) * (180.0 / M_PI))
and pass the value to the function:
-(UIImage*)rotateImage:(UIImage*)img forAngle:(CGFloat)degree {
UIView *rotatedViewBox = [[UIView alloc]initWithFrame:CGRectMake(0, 0, img.size.width, img.size.height)];
CGAffineTransform t = CGAffineTransformMakeRotation(degree);
rotatedViewBox.transform = t;
CGSize rotatedSize = rotatedViewBox.frame.size;
UIGraphicsBeginImageContext(rotatedSize);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, rotatedSize.width, rotatedSize.height);
CGContextRotateCTM(context, radian);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, CGRectMake(-img.size.width, -img.size.height, img.size.width, img.size.height), img.CGImage);
UIImage *returnImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return returnImg;
}

My CALayer transform holds after animation, but the perspective disappears

I have the following code that rotates a CALayer by -45degrees on the Y axis:
#define D2R(x) (x * (M_PI/180.0))
- (void) swipe:(UISwipeGestureRecognizer *)recognizer
{
CATransform3D transform = CATransform3DMakeRotation(D2R(-45), 0, 1.0, 0);
transform.m34 = -1.0 / 850;
CABasicAnimation *transformAnimation = [CABasicAnimation animationWithKeyPath: #"transform"];
transformAnimation.fillMode = kCAFillModeForwards;
transformAnimation.removedOnCompletion = NO;
transformAnimation.toValue = [NSValue valueWithCATransform3D:transform];
transformAnimation.duration = 0.5;
[self.layer addAnimation:transformAnimation forKey:#"transform"];
}
The animation works, except it ends with no perspective - ignoring my m34 setting if I'm understanding things correctly.
Halfway through:
At the end:
What am I doing wrong?
Animation only affects the appearance of the view during the animation. It doesn't get applied to the view after the animation ends. You need to do this yourself. I'm guessing something like this right after adding the animation will work:
self.layer.transform = transform;
You can do this right away, as the animation will hide it until the animation completes.
Try this :
- (void) swipe:(UISwipeGestureRecognizer *)recognizer
{
CATransform3D transform = CATransform3DIdentity;
transform.m34 = -10 / 850.0;
transform = CATransform3DRotate(transform, D2R(-45), 0, 1.0, 0);
CABasicAnimation *transformAnimation = [CABasicAnimation animationWithKeyPath: #"transform"];
transformAnimation.fillMode = kCAFillModeForwards;
transformAnimation.removedOnCompletion = NO;
transformAnimation.toValue = [NSValue valueWithCATransform3D:transform];
transformAnimation.duration = 0.5;
[self.layer addAnimation:transformAnimation forKey:#"transform"];
}
And end the effect is like this :

CAAnimationGroup not working

I have a problem with CAAnimationGroup. I have a view called animeView and I want to apply a 3D rotation around Y axis and a scaling transform at the same time. The rotation and scaling animations work perfectly fine separately! But when I add them to a CAAnimationGroup Non of the animations occur!
What is the problem?
CAKeyframeAnimation *rotation = [CAKeyframeAnimation animation];
CATransform3D rotationTransform = CATransform3DMakeRotation(M_PI, 0.0f, 1.0f, 0.0f);
rotationTransform.m34 = 1.0 / -500;
rotation.values = [NSArray arrayWithObjects:
[NSValue valueWithCATransform3D:CATransform3DMakeRotation(0.0f, 0.0f, 1.0f, 0.0f)],
[NSValue valueWithCATransform3D:rotationTransform],
[NSValue valueWithCATransform3D:CATransform3DMakeRotation(2.0*M_PI, 0.0f, 1.0f, 0.0f)] ,nil];
CAKeyframeAnimation *trans = [CAKeyframeAnimation animation];
trans.values = [NSArray arrayWithObjects:[NSValue valueWithCATransform3D:CATransform3DIdentity],[NSValue valueWithCATransform3D:CATransform3DMakeScale(0.5, 0.5, 0.5)],[NSValue valueWithCATransform3D:CATransform3DIdentity], nil];
CAAnimationGroup *group = [CAAnimationGroup animation];
group.delegate = self;
[group setAnimations: [NSArray arrayWithObjects:rotation, trans, nil]];
group.duration = 2;
group.repeatCount = 3;
[self.animView.layer addAnimation:group forKey:nil];
There are two things that is strange with the code you've provided.
The animations doesn't know what property they should be changing (no key path is set)
Both animations are changing the transform (what value should really be used?)
The way I see it is that you have two transform key-frame animations with the same number of values (three). Therefore you should calculate the total transformation matrix (both scale and rotate in one matrix) and only add that animation to your view.
It would look something like this (I've added lots of comments to explain what sis happening):
// Your first rotation is 0 degrees and no scaling
CATransform3D beginTransform = CATransform3DIdentity;
// Your middle rotation is the exact one that you provided ...
CATransform3D middleTransform = CATransform3DMakeRotation(M_PI, 0.0f, 1.0f, 0.0f);
// ... but the rotation matrix is then scaled as well ...
middleTransform = CATransform3DScale(middleTransform, 0.5, 0.5, 0.5);
// ... and the perspective is set
middleTransform.m34 = 1.0 / -500;
// Your end value is rotated but not scaled
CATransform3D endTransform = CATransform3DMakeRotation(2.0*M_PI, 0.0f, 1.0f, 0.0f);
// Create a new animation that should animate the "transform" property (thus the key path "transform")
CAKeyframeAnimation *rotateAndScale = [CAKeyframeAnimation animationWithKeyPath:#"transform"];
// same configurations as your group had
[rotateAndScale setDuration:2.0];
[rotateAndScale setRepeatDuration:3.0];
[rotateAndScale setDelegate:self];
// Set the values of the transform animation (yes, both scaling and rotation in one animation)
[rotateAndScale setValues:[NSArray arrayWithObjects:
[NSValue valueWithCATransform3D:beginTransform],
[NSValue valueWithCATransform3D:middleTransform],
[NSValue valueWithCATransform3D:endTransform], nil]];
// Add the animation to the layer, no need for a group here...
[animView.layer addAnimation:rotateAndScale forKey:nil];

Resources