CATransaction (still) Not Animating - iphone

I have a problem very similar to this one :
CATransaction Not Animating
I'm just trying to animate a view layer using CATransaction. My problem is that the transform is applied to the view immediatly.
I tried to perform the animation using performSelector:withObject:afterDelay: without success.
Here is my code :
- (void)viewDidLoad {
[super viewDidLoad];
view = [[[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)] autorelease];
view.backgroundColor = [UIColor blackColor];
[self.view addSubview:view];
[self performSelector:#selector(animateView) withObject:nil afterDelay:0.1];
}
-(void) animateView {
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithFloat:3.0f] forKey:kCATransactionAnimationDuration];
CALayer *layer = view.layer;
layer.position = CGPointMake(20,
300);
CATransform3D transform = CATransform3DMakeScale(2.0f, 2.0f, 1.0f);
transform = CATransform3DRotate(transform, acos(-1.0f)*1.5f, 1.5f, 1.5f, 1.5f);
layer.transform = transform;
[CATransaction commit];
}
Does anybody knows what is going wrong ?
Thanks, Vincent.

when animating a view's backing layer, you need to be inside a UIView animation block, not just a CATransaction:
[UIView beginAnimations:nil context:NULL];
// ... your animation code
[UIView commitAnimations];

or you could use CAAnimationGroup and group the scale and rotation animations like this..
CAAnimationGroup *aniGroup = [CAAnimationGroup animation];
// setup aniGroup properties
CABasicAnimation *scaleAnimation = [CABasicAnimation defaultValueForKey:#"transform.scale"];
scaleAnimation.fromValue = [NSNumber numberWithFloat:0.0];
scaleAnimation.toValue = [NSNumber numberWithFloat:1.0];
// setup scaleAnimation properties
CABasicAnimation *rotateAnimation = [CABasicAnimation defaultValueForKey:#"transform.rotate"];
// setup rotateAnimation properties
// ....
aniGroup.animations = [NSArray arrayWithObjects:rotateAnimation, scaleAnimation, nil];
[self.layer layoutIfNeeded];
[self.layer addAnimation:aniGroup forKey:#"myAnimation"];
Something like that.... Might not be exactly copy, paste, and build-able. ;)
Hope this helps!

Related

Rotate a UIView clockwise for an angle greater than 180 degrees

I'm animating a clock arm from pointing towards 12 o'clock to the current time. If it is say, 11 o'clock, I want the arm to rotate clockwise to the 11 o'clock position. But of course if I use:
CGAffineTransform rotation = CGAffineTransformMakeRotation(2*M_PI*11/12);
[UIView animateWithDuration:3.0
animations:^{
clockArm.transform = rotation;
}];
the rotation goes counterclockwise. I tried:
CGFloat angle = 2*M_PI*11/12;
CGAffineTransform firstRotation = CGAffineTransformMakeRotation(M_PI-0.001);
CGFloat firstRotationTime = 3.0*(M_PI/angle);
CGAffineTransform secondRotation = CGAffineTransformMakeRotation(angle);
CGFloat secondRotationTime = 3.0 - firstRotationTime;
[UIView animateWithDuration:firstRotationTime
delay:0.0
options:UIViewAnimationCurveLinear
animations:^{
self.clockArm1.transform = firstRotation;
}
completion:^(BOOL finished) {
[UIView animateWithDuration:secondRotationTime
delay:0.0
options:UIViewAnimationCurveLinear
animations:^{
self.clockArm1.transform = secondRotation;
}
completion:^(BOOL finished){
}];
}];
The animation does go clockwise, but it is choppy - the animation still seems to be doing a UIViewAnimationEaseInOut for the first animation. What am I doing wrong, or is there another way to accomplish what I want?
You can use the completion block of CATransaction to set the rotation property of the view when the animation has finished. The following function worked in my test case:
- (void) rotateViewAnimated:(UIView*)view
withDuration:(CFTimeInterval)duration
byAngle:(CGFloat)angle
{
[CATransaction begin];
CABasicAnimation *rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
rotationAnimation.byValue = [NSNumber numberWithFloat:angle];
rotationAnimation.duration = duration;
rotationAnimation.removedOnCompletion = YES;
[CATransaction setCompletionBlock:^{
view.transform = CGAffineTransformRotate(view.transform, angle);
}];
[view.layer addAnimation:rotationAnimation forKey:#"rotationAnimation"];
[CATransaction commit];
}
You use it like
[self rotateViewAnimated:self.clockArm1 withDuration:3.0 byAngle:2*M_PI*11./12.];
Thanks for #Martin R 's accepted answer. I edited a bit:
I replaced this line
rotationAnimation.removedOnCompletion = YES;
with these two lines:
rotationAnimation.removedOnCompletion = NO;
rotationAnimation.fillMode = kCAFillModeForwards;
Then, in the CompletionBlock, I added one more line:
[view.layer removeAllAnimations];
So, the code finally becomes:
- (void) rotateViewAnimated:(UIView*)view
withDuration:(CFTimeInterval)duration
byAngle:(CGFloat)angle
{
[CATransaction begin];
CABasicAnimation *rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
rotationAnimation.byValue = [NSNumber numberWithFloat:angle];
rotationAnimation.duration = duration;
// changed by me
rotationAnimation.removedOnCompletion = NO;
rotationAnimation.fillMode = kCAFillModeForwards;
[CATransaction setCompletionBlock:^{
view.transform = CGAffineTransformRotate(view.transform, angle);
// added by me
[view.layer removeAllAnimations]; // this is important
}];
[view.layer addAnimation:rotationAnimation forKey:#"rotationAnimation"];
[CATransaction commit];
}
My references are from:
1, https://stackoverflow.com/a/3586433/2481444
2, After rotating a CALayer using CABasicAnimation the layer jumps back to it's unrotated position
As mentioned in comments, try this iphone UIImageView rotation
or UIView Infinite 360 degree rotation animation?
#import <QuartzCore/QuartzCore.h>
- (void) runSpinAnimationWithDuration:(CGFloat) duration;
{
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 = 1.0;
rotationAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
[myView.layer addAnimation:rotationAnimation forKey:#"rotationAnimation"];
}

Animation, like when you delete from iPhone Photo Gallery

I try to implement the animation:
when you enter iPhone Gallery, press the image, you see full-screen image. Below you can see toolbar with trash button. When you press this button, the image is being deleted with animation.
I try to implement this, but I don't know, how to implement the transform of image, apple use.
This is the best, I could do:
[UIView transitionWithView:self.view duration:0.1 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
[self.view addSubview:scrollImageView];
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
CGRect frame = scrollImageView.frame;
frame.size = CGSizeMake(frame.size.width * 0.75, frame.size.height * 0.75);
frame.origin = CGPointMake((size.width - frame.size.width) / 2, (size.height - frame.size.height) / 2);
scrollImageView.frame = frame;
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
CGRect frame = scrollImageView.frame;
frame.size = CGSizeMake(frame.size.width * 0.05, frame.size.height * 0.05);
frame.origin = CGPointMake(size.width, size.height);
scrollImageView.frame = frame;
CGAffineTransform transform = scrollImageView.transform;
CGAffineTransform rotatedTransform = CGAffineTransformRotate(transform, 45 * 3.14 / 180);
scrollImageView.transform = rotatedTransform;
} completion:^(BOOL finished) {
[scrollImageView removeFromSuperview];
}];
}];
}];
Thank you in advance.
Update
As I understand, I can't do this animation with Core-Animation, but may anyone can advice me the animation the most simular to iPhone Gallery animation, but without using OpenGL?
You can use following example for this animation:
UIView *senderView = (UIView*)sender;
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"transform"];
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
anim.duration = 0.125;
anim.repeatCount = 1;
anim.autoreverses = YES;
anim.removedOnCompletion = YES;
anim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.2, 1.2, 1.0)];
//[senderView.layer addAnimation:anim forKey:nil];
UIBezierPath *movePath = [UIBezierPath bezierPath];
[movePath moveToPoint:icon.center];
[movePath addQuadCurveToPoint:senderView.center
controlPoint:CGPointMake(senderView.center.x, icon.center.y)];
CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:#"position"];
moveAnim.path = movePath.CGPath;
moveAnim.removedOnCompletion = YES;
CABasicAnimation *scaleAnim = [CABasicAnimation animationWithKeyPath:#"transform"];
scaleAnim.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
scaleAnim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.1, 0.1, 1.0)];
scaleAnim.removedOnCompletion = YES;
CABasicAnimation *opacityAnim = [CABasicAnimation animationWithKeyPath:#"alpha"];
opacityAnim.fromValue = [NSNumber numberWithFloat:1.0];
opacityAnim.toValue = [NSNumber numberWithFloat:0.1];
opacityAnim.removedOnCompletion = YES;
CAAnimationGroup *animGroup = [CAAnimationGroup animation];
animGroup.animations = [NSArray arrayWithObjects:moveAnim, scaleAnim, opacityAnim, nil];
animGroup.duration = 0.5;
[icon.layer addAnimation:animGroup forKey:nil];
I have modified the code, you have to perform following changes in it, set the sender view as self.view, and change the ending point of animation (which is currently senderView.center) according to your requirement
I know its a bit late. But, you should check Ciechan's solution named BCGenieEffect. Can be found here. Its pure Core Animation and very easy to understand. I think that's what you are looking for.
Good luck
At this point the exact animation you are talking about cannot be done using Core Animation or UIKit. You would need to use OpenGL and apply the image as a texture and do your animation in there.
I think you are talking about the "suck" transition animation.
It is possible to trigger it, but it is a private transition, and Apple may reject your app if you use it.
This code should do it, using a transition code of 103:
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.0];
[UIView setAnimationTransition: transtionIndex forView:containerView cache:NO];
[containerView addSubview: newView];
[oldView removeFromSuperview];
[UIView commitAnimations];
Or search the net for a Core Image transition called "suckEffect"

Animate multiple images in iphone with CA

i have a UIViewController which contains the 16 UIImageView now i have to annimate 4 images with Core Animation
any idea how to do this ?
i am able to animate the one only following is my code
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"position"];
anim.fromValue = [NSValue valueWithCGPoint:CGPointMake(100, 100)];
anim.toValue = [NSValue valueWithCGPoint:CGPointMake(200, 200)];
anim.duration = 1.5f;
anim.repeatCount =1;
anim.removedOnCompletion = YES;
anim.autoreverses = YES;
anim.repeatCount = 10;
anim.timingFunction = [CAMediaTimingFunction
functionWithName:kCAMediaTimingFunctionEaseIn];
[imgview.layer addAnimation:anim forKey:#"position"];
Here's a theory solution in my mind, that in a loop from (1 to [yourImageArray count]) do load an imageView dynamically, load single image from yourImage array using index from that loop. Perform some animation using code on imageView.
UIImageViews are subclasses of UIView, which have the following method:
animateWithDuration:delay:options:animations:completion
You can use it in the following way:
[UIView animateWithDuration:1.5f
delay:0.0f
options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat
animations:^{
[UIView setAnimationRepeatCount:10];
[imgview1 setCenter:CGPointMake(200,200)];
[imgview2 setCenter:CGPointMake(300,200)];
[imgview3 setCenter:CGPointMake(400,200)];
[imgview4 setCenter:CGPointMake(500,200)];
};
completion:nil];

CAAnimation with performSelector afterDelay

I am messing around with a basic CAAnimation example where Im just trying to get a CALayer to rotate in a subclass of UIView. I start by making a new CALayer with a red background and inserting it into self.layer. I then make a CAAnimation that is supposed to be triggered on opacity changes and apply it to the CALayer, after which it spins as expected.
Weird thing is that if I try to apply the exact same animation using performSelector:withObject:afterDelay:, the CALayer no longer animates, although its opacity still changes. Even weirder is that if I use performSelector:withObject:afterDelay: to animate self.layer using pretty much the same code, it works.
Any ideas as to what's going on? Here is my code:
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
point = [[CAGradientLayer alloc] init];
point.backgroundColor = [UIColor redColor].CGColor;
point.bounds = CGRectMake(0.0f, 0.0f, 30.0f, 20.0f);
point.position = CGPointMake(100.0f, 100.0f);
[self.layer addSublayer:point];
[point release];
[self performSelector:#selector(test) withObject:nil afterDelay:2];
}
return self;
}
-(void)test {
[self spinLayer:point];
// [self spinLayer:self.layer]; works here
}
-(CAAnimation*)animationForSpinning {
CATransform3D transform;
transform = CATransform3DMakeRotation(M_PI/2.0, 0, 0, 1.0);
CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:#"transform"];
basicAnimation.toValue = [NSValue valueWithCATransform3D:transform];
basicAnimation.duration = 5;
basicAnimation.cumulative = NO;
basicAnimation.repeatCount = 10000;
return basicAnimation;
}
-(void)spinLayer:(CALayer*)layer {
CAAnimation *anim = [self animationForSpinning];
[layer addAnimation:anim forKey:#"opacity"];
layer.opacity = 0.6;
}
and this is my interface
#interface MyView : UIView {
CALayer *point;
}
-(void)test;
-(void)spinLayer:(CALayer*)layer;
-(CAAnimation*)animationForSpinning;
#end
Note: I am instantiating this view with a frame equal to [UIScreen mainScreen].applicationFrame from within the app delegate.
Change this:
[layer addAnimation:anim forKey:#"opacity"];
into this:
[layer addAnimation:anim
forKey:#"notOpacity!"];
What happens is that implicit animation layer.opacity = 0.6 overwrites you explicit animation. This happens because Core Animation uses property names when adding implicit animations.
(If you move layer.opacity = 0.6 above [layer addAnimation:anim forKey:#"opacity"]; it will also work, but layer.opacity will not get animated.)
And why it works without delay: because implicit animations are "enabled" only if layer is in the tree and been displayed.

CABasicAnimation Problem

So, I have read in the docs, that use of blocks like
beginAnimation
commitAnimation
is discouraged from os4.0.
So I have tried to get my code to work by using CABasicAnimation. I want to achieve, that an image's frame is resized from its thumbnail size, somewhere within my view, to a full width position e.g. (0, 120, 320, 240) - on my iPhone.
What I have so far:
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithFloat:1.0] forKey:kCATransactionAnimationDuration];
CABasicAnimation *scalingAnimation;
scalingAnimation = [CABasicAnimation animationWithKeyPath:#"transform"];
scalingAnimation.duration=1.0/2;
scalingAnimation.autoreverses=YES;
scalingAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
scalingAnimation.fromValue=[NSValue valueWithCATransform3D:CATransform3DMakeScale(1.0, 1.0, 1.0)];
scalingAnimation.toValue=[NSValue valueWithCATransform3D:CATransform3DMakeScale(4, 4, 1)];
[b.layer addAnimation:scalingAnimation forKey:#"scaling"];
[CATransaction commit];
My nextstep would be to first try to move the image to a centered position then scale it to the correct size. However, I doubt I'm doin it the right way. Can anyone comment on my code/approach.... is there a better way?
Am pretty sure you have solved this by now, but in any case.
You shouldn't need the [CATransaction begin]; and [CATransaction commit];
The simplest way that I have found to do this kind of thing is to use CAAnimationGroup and and add the animations one by one.
An example would be
CABasicAnimation *scaleX = [CABasicAnimation animationWithKeyPath:#"transform.scale.x"];
//this is not used, as the group provides the duration
scaleX.duration = duration;
scaleX.autoreverses = NO;
scaleX.toValue = [NSNumber numberWithFloat:1.0];
scaleX.fromValue = [NSNumber numberWithFloat:scaleFrom];
scaleX.fillMode = kCAFillModeForwards;
scaleX.removedOnCompletion = NO;
CABasicAnimation *scaleY = [CABasicAnimation animationWithKeyPath:#"transform.scale.y"];
//this is not used, as the group provides the duration
scaleY.duration = duration;
scaleY.autoreverses = NO;
scaleY.toValue = [NSNumber numberWithFloat:1.0];
scaleY.fromValue = [NSNumber numberWithFloat:scaleFrom];
scaleY.fillMode = kCAFillModeForwards;
scaleY.removedOnCompletion = NO;
//add in the translation animations
NSMutableArray* animationsArray = [NSMutableArray arrayWithObjects:scaleX,
scaleY,
//and any other animations you want in the group
nil];
CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
animationGroup.duration = 1.0/2;
animationGroup.timingFunction = [CAMediaTimingFunctionfunctionWithName:kCAMediaTimingFunctionEaseIn];
animationGroup.animations = animationsArray;
animationGroup.delegate = self;
animationGroup.removedOnCompletion = YES;
animationGroup.fillMode = kCAFillModeForwards;
[animationGroup setValue:#"imageTransform" forKey:#"AnimationName"];
[b.layer addAnimation:animationGroup forKey:#"imageTransform"];
There are a few gotchas though. Animations are purely visual, so before running the animations, set your eventual view frame.
You will notice that the scales end at 1, this is to ensure that you dont end up with a scaled image layer. Instead we start it scaled and bring it back to normal.
Translations should be done in the same way.
Hope this helps
//in Event Method Copy Below code to place the image to the center
CABasicAnimation* positionAnimation;
positionAnimation = [CABasicAnimation animationWithKeyPath:#"position"];
positionAnimation.fromValue = [NSValue valueWithCGPoint:imageView.layer.position];
positionAnimation.toValue = [NSValue valueWithCGPoint:centerPointofView];
positionAnimation.duration = 2.0;
positionAnimation.fillMode = kCAFillModeForwards;
positionAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
positionAnimation.removedOnCompletion = NO;
positionAnimation.delegate = self;
[imageView.layer addAnimation:positionAnimation forKey:#"positionAnimation"];
// For Scale After image is in center copy below code
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag{
CAAnimation *animation = [imageView.layer animationForKey:#"positionAnimation"];
if (animation == theAnimation)
{
CABasicAnimation* scaleAnimation;
scaleAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
scaleAnimation.fromValue = [NSNumber numberWithFloat:1];
scaleAnimation.toValue = [NSNumber numberWithFloat:4.0];
scaleAnimation.duration = 2.2;
scaleAnimation.fillMode = kCAFillModeForwards;
scaleAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
scaleAnimation.removedOnCompletion = NO;
scaleAnimation.delegate = self;
[imageView.layer addAnimation:scaleAnimation forKey:#"scaleAnimation"];
}
else
{
//if you want changes to image should remain forever
//place your code for scale & transform here
....................
//now simple remove animation from layer
[imageView.layer removeAllAnimations];
}
}
Cant you use
[UIView animateWithDuration:2.0
animations:^{
//animations
}
completion:^(BOOL finished){
// completion methods
}];