I have this code to move a view.
CABasicAnimation *theAnimation;
theAnimation=[CABasicAnimation animationWithKeyPath:#"transform.translation.x"];
theAnimation.duration=1;
theAnimation.repeatCount=1;
theAnimation.autoreverses=NO;
theAnimation.fromValue=[NSNumber numberWithFloat:0];
theAnimation.toValue=[NSNumber numberWithFloat:-60];
[view.layer addAnimation:theAnimation forKey:#"animateLayer"];
The problem is: The actual coordinates of the view get reseted after the animation has finished. Is it possible that the view remains at the new coordinates after the animation has finished?
Thanks.
In addition to attaching an animation that animates the property, you have to actually set the property to its final value. Crazy, isn't it? Try doing this before you add the animation:
[view.layer setValue:[NSNumber numberWithFloat:-60] forKey:#"transform.translation.x"];
I strongly recommend watching the WWDC 2011 video "Core Animation Essentials". It explains this, and covers a lot of useful information for anyone using Core Animation. You can find it here: https://developer.apple.com/videos/wwdc/2011/
Related
I have a UIView. I applied the animation to its CALayer.
[view.layer addAnimation:groupAnimation forKey:name];
I want the final state of the layer to be the state of the UIView after the animation. Let's say I rotated by 45degrees and moved to a new position using the layer; is it possible for my view to be in that state after the animation? Because right now, after the animation, it goes back to the original state of the UIView. I hope to receive some help with this. Thanks.
The thing to understand is that layer animation is just animation; it's merely a kind of temporary "movie" covering the screen. When the animation ends, the "movie" is removed, thus revealing the true situation. It is up to you to match that situation with the final frame of the movie.
UIView animation does this for you, to a great extent. But layer animation leaves it entirely up to you.
Thus, let's say you animate a position change; doing something so that the layer or view actually is where you animated to is completely up to you.
The usual thing is to perform the changes yourself as a separate set of commands. But be careful not to do anything that might trigger implicit layer animation, as this will conflict with the animation you are trying to perform explicitly.
Here's example code (not related to yours, but it does show the general form):
CAAnimationGroup* group = // ...
// ... configure the animation ...
[view.layer addAnimation:group forKey:nil];
// now we are ready to set up the view to look like the final "frame" of the animation
[CATransaction setDisableActions:YES]; // do not trigger implicit animation by mistake
view.layer.position = finalPosition; // assume we have worked this out
When animating a CALayer or using a CAAnimationGroup, the following properties must be set, e.g.:
groupAnimation.removedOnCompletion = NO;
groupAnimation.fillMode = kCAFillModeForwards;
See also: After Animation, View position resets
Also note that it may be helpful to apply animations directly to the view itself, rather than by accessing the view's layer. This is accomplished using animation blocks, which I have found to be very useful.
Block style animations can be customized in many ways, but here's a basic example, which could be invoked within a function when your view needs to animate:
- (void) animateMyView
{
CGRect newViewFrame = CGRectMake(x,y,w,h);
[UIView animateWithDuration:0.5
delay:0
options: (UIViewAnimationOptionCurveLinear )
animations:^{
self.myView = CGAffineTransformMakeRotation (45);
[self.myView setBounds:newViewFrame];
}
completion:NULL];
}
For more information, see Apple's documentation on View Animation.
My app uses CALayer to draw views. More precisely, it uses the drawLayer:inContext: method on a sublayer of a UIView's top layer. This is a nice way to get the 'implicit' animation of consecutive drawLayer:inContext: drawings to fade into each other over time. The fading animations happen fairly fast, maybe in 0.25 seconds, but to change its duration, simply implement another delegate method called actionForLayer:forKey:. In this perfectly working example implementation here the default duration is stretched to 2.0 seconds:
- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event
{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:event];
animation.duration = 2.0;
return animation;
// or return nil for the default duration.
}
On to the issue at hand.
If you call [sublayer setNeedsDisplay] faster than the fades have time to complete, with each new fade you'll see a sudden jump. From the looks of it, the fade that's in progress is cancelled and it's final state is used as the starting point of the new fade. This might not be very surprising, but the visual result is rather unwanted.
Consider the scenario of a ten second fade from black to white, with another fade, to black, triggered five seconds after the start. The animation will start fading from black to white, but when it's at a 'half way gray' it jumps to full white before fading to black again.
Is there a way to prevent this from happening? Can I get the layer to fade from the gray back down to black? Is there a CALayer drawing equivalent of saying UIViewAnimationOptionBeginFromCurrentState (used in UIView animations)?
Cheers.
A layer's animation is only a visual representation of what the layer should look like as it animates. In CA when you animate from one state to another, the entire state of the layer changes immediately. A presentation layer is created and displays the animation, and when the animation completes the actual layer is left in place at the end.
So, my guess is that when you want to transition from one state to another, and the current animation hasn't completed yet, you have to capture the current state of the animation and then use this as the starting point for your next animation.
The problem lies in not being able to modify a layer's current animation.
In the following post I capture the current state of an animation, set that as the current state for the layer and use that as the beginning value from which to animate. The post applies this technique to the speed / duration of an animation, but can also be applied to your scenario.
https://stackoverflow.com/a/9544674/1218605
I'm a little stumped on this one too.
Did you forget to specify the fillMode kCAFillModeForwards. There's more info about that in the reference docs.
For example, I got this to work without any snapping, although I'm not changing the duration.
#implementation FadingLayer
- (void)fadeOut {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"backgroundColor"];
animation.fillMode = kCAFillModeForwards;
animation.fromValue = (id)[UIColor redColor].CGColor;
animation.toValue = (id)[UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:0.0].CGColor;
animation.removedOnCompletion = FALSE;
animation.delegate = self;
[self addAnimation:animation
forKey:#"test"];
}
- (void)fadeIn {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"backgroundColor"];
animation.fillMode = kCAFillModeForwards;
animation.fromValue = (id)[UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:0.0].CGColor;
animation.toValue = (id)[UIColor redColor].CGColor;
animation.removedOnCompletion = FALSE;
animation.delegate = self;
[self addAnimation:animation
forKey:#"test"];
}
#end
You'll probably want to animate a custom property however.
Hope this helps :/
I wanted to accomplish the same thing with a zoom animation of a layer tree. I have a zoom in/out key-equivalent where the user can zoom the layer tree accordingly. However, if the user presses the zoom key-equivalent in rapid succession, there would be a temporary snap-back to the values prior to the onset of the animation, since the previous animation hadn't yet completed.
At the end of the animation code, performing a sole [CATransaction commit] forced any pending transactions to be committed to the layer model before the start of the next animation, and solved the problem.
The documentation says:
+ commit
Commit all changes made during the current transaction.
Declaration
+ (void)commit
Special Considerations
Raises an exception if no current transaction exists.
However, testing this with many [CATransaction commit] messages in succession doesn't actually raise an exception. I've used this same technique to squelch warnings of the form:
CoreAnimation: warning, deleted thread with uncommitted
CATransaction;
in an NSOperation whose thread of execution finishes before layer animations do. It could be that Apple changed this behaviour in recent OS releases to a no-op (which would be much saner) if no current transaction exists, without updating the documentation.
Hi i am working on an iPhone application and i have to animate and rotate an image, Image is attached here
now purpose of this image is that whenever any tap some where on iPad it will animate and will point towards that place.
Please help me to figure it out. Thanx
Try this -
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
animation.fromValue = [NSNumber numberWithFloat:0.0f];
animation.toValue = [NSNumber numberWithFloat: 2*M_PI];
animation.duration = 10.0f;
animation.repeatCount = INFINITY;
[self.yourImage.layer addAnimation:animation forKey:#"SpinAnimation"];
check this tutorial. It will really help you.
To animate images view, you'll need to use CoreAnimation.
All you have to do is making UIImageView object with your image inside and then you'll manipulate directly the layer to animate.
There is 2 kind of animation with core animation :
Implicit animation (normally they are activate by default) : Everytime you change some setting on the layer (like position with [imageView.layer setPosition:CGPointMake...]) there should be an implicit animation with default parameters (Duration, AnimationCurve etc.)
Explicit animation : If you want to create more complex animations, you should use explicit animation, it will allow you to do complex things like moving, rotating and scaling some layer in the same time.
I encourage you to check the core animation programming guide
This is my problem:
The background of the progress indicator doesn't appear to be redrawing, and it's certainely not transparent. I'm using core animation to animate the image in the background; when I don't use core animation it looks fine. This is the code I am using:
[[NSAnimationContext currentContext] setDuration:0.25];
[[ViewImage animator] setAlphaValue:0.5f ];
[[statusText animator] setAlphaValue:0.1f ];
[progressIndicator usesThreadedAnimation ];
The progress indicator doesn't use core animation. I have also tried removing [progressIndicator usesThreadedAnimation]; which doesn't help.
-usesThreadedAnimation is the getter for the property. You want -setUsesThreadedAnimation: to set the property.
Also, for the transparency issue, I believe you need to switch on layers for at least the progress indicator if not the parent view as well. That should fix the transparency issue.
I have two circles which move around the screen. The circles are both UIViews which contain other UIViews. The area outside each circle is transparent.
I have written a function to create a CGPath which connects the two circles with a quadrilateral shape. I fill this path in a transparent CALayer which spans the entire screen. Since the layer is behind the two circular UIViews, it appears to connect them.
Finally, the two UIViews are animated using Core Animation. The position and size of both circles change during this animation.
So far the only method that I have had any success with is to interrupt the animation at regular intervals using an NSTimer, then recompute and draw the beam based on the location of the circle's presentationLayer. However, the quadrilateral lags behind the circles when the animation speeds up.
Is there a better way to accomplish this using Core Animation? Or should I avoid Core Animation and implement my own animation using an NSTimer?
I faced a similar problem. I used layers instead of views for the animation. You could try something like this.
Draw each element as a CALayer and include them as sublayers for your container UIVIew's layer. UIViews are easier to animate, but you will have less control. Notice that for any view you can get it's layer with [view layer];
Create a custom sublayer for your quadrilateral. This layer should have a property or several of properties you want to animate for this layer. Let's call this property "customprop". Because it is a custom layer, you want to redraw on each frame of the animation. For the properties you plan to animate, your custom layer class should return YES needsDisplayForKey:. That way you ensure -(void)drawInContext:(CGContextRef)theContext gets called on every frame.
Put all animations (both circles and the quad) in the same transaction;
For the circles you can probably use CALayers and set the content, if it is an image, the standard way:
layer.contents = [UIImage imageNamed:#"circle_image.png"].CGImage;
Now, for the quad layer, subclass CALayer and implement this way:
- (void)drawInContext:(CGContextRef)theContext{
//Custom draw code here
}
+ (BOOL)needsDisplayForKey:(NSString *)key{
if ([key isEqualToString:#"customprop"])
return YES;
return [super needsDisplayForKey:key];
}
The transaction would look like:
[CATransaction begin];
CABasicAnimation *theAnimation=[CABasicAnimation animationWithKeyPath:#"customprop"];
theAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(1000, 1000)];
theAnimation.duration=1.0;
theAnimation.repeatCount=4;
theAnimation.autoreverses=YES;
theAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
theAnimation.delegate = self;
[lay addAnimation:theAnimation forKey:#"selecting"];
[CATransaction setValue:[NSNumber numberWithFloat:10.0f]
forKey:kCATransactionAnimationDuration];
circ1.position=CGPointMake(1000, 1000);
circ2.position=CGPointMake(1000, 1000);
[CATransaction commit];
Now all the draw routines will happen at the same time. Make sure your drawInContext: implementation is fast. Otherwise the animation will lag.
After adding each sublayer to the UIViews's layer, rememeber to call [layer setNeedsDisplay]. It does not get called automatically.
I know this is a bit complicated. However, the resulting animations are better than using a NSTimer and redrawing on each call.
If you need to find the current visible state of the layers, you can call -presentationLayer on the CALayer in question, and this will give you a layer that approximates the one used for rendering. Note I said approximates - it's not guaranteed to be fully accurate. However it may be good enough for your purposes.