Continue animation from where it was interupted - Core animation - ios

I need a a little help with my animation.
I got two buttons, the first button calls an animation and the second one calls another animation.
Now when I press the first button a code like this one down below will be called.
The Problem is that when I press the second button the animation kinda teleports to it's starting position (second animations position), is there a way to make the animations flow together from where it was interupted?
CABasicAnimation *anim1 = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
anim1.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
anim1.fromValue = [NSNumber numberWithFloat:((-10*M_PI)/180)];
anim1.toValue = [NSNumber numberWithFloat:((10*M_PI)/180)];
anim1.repeatCount = HUGE_VALF;
anim1.autoreverses = YES;
anim1.duration = .5;
[head addAnimation:anim1 forKey:#"transform"];
Virtual picture:
http://i47.tinypic.com/2yopif6.jpg
The black line is the first animation, the red is the second animation, and the green is the one I'm trying to figure out. According to the picture it was interupted in the middle of the black.

Why not using the animation method instead of the UIView class - just make sure you use the UIViewAnimationOptionBeginFromCurrentState option?
Check the reference doc here: http://developer.apple.com/library/ios/#documentation/uikit/reference/uiview_class/uiview/uiview.html
EDIT
I think this tutorial will help you better: http://wangling.me/2011/06/core-animation-101-from-and-to/. What they are trying to achieve there is to bounce a ball after triggering the animation through a button. When the button is pressed a second time the ball should bounce back from the current position. There is a section where they mention they rely on 2 delegates method animationDidStart and animationDidStop to implement this desired effect.

Related

How to properly set the model when doing CABeginAnimation

I have a UIImageView that when the user taps it, a border of 4 points toggles on and off. I'm trying to animate the border in and out as follows:
CABasicAnimation *widthAnimation = [CABasicAnimation animationWithKeyPath:#"borderWidth"];
widthAnimation.toValue = self.isSelected ? #4.0 : #0.0;
widthAnimation.duration = 0.1;
[self.imageView.layer addAnimation:widthAnimation forKey:#"borderWidth"];
Now, as I've learned from research and scouring SO, CABasicAnimation just changes the presentation layer, but not the actual model. I've also read that using fillMode and removedOnCompletion is bad practice, since it leads to inconsistencies between the model and what the user sees. So, I tried to change the model with the following line:
self.imageView.layer.borderWidth = self.isSelected ? 4.0 : 0.0;
The problem is, this line seems to set the property straight away, so by the time the animation kicks in, the border width is already at it's desired value. I've tried sticking this line at the beginning of the code, end, and everywhere in between, but to no success. I did manage to find a hacky solution: instead of setting the property, I passed the property setter to performSelector: withObject: afterDelay:, with the delay being the duration of the animation. This works most of the time, but sometimes the cycles don't quite match up, and the animation will run first, then it jumps back to the original state, then it snaps to the new state, presumably as a result of performSelector
So is there any way to smoothly animate a border without performSelector?
Any help is greatly appreciated.
Here is an example of CABasicAnimation I made a while ago :
-(void) animateProgressFrom:(CGFloat)fromValue to:(CGFloat)toValue
{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"opacity"];
animation.fromValue = #(fromValue);
animation.toValue = #(toValue);
animation.duration = ABS(toValue - fromValue)*3.0;
[self.layer addAnimation:animation forKey:#"opacity"];
[CATransaction begin];
[CATransaction setDisableActions:YES];
self.layer.opacity = toValue;
[CATransaction commit];
}
I think what you needed is the CATransaction at the end of the layer animation.

CABasicAnimation stop animation on completion - iOS

I have an iOS app which is using a CABasicAnimation on repeat:
CABasicAnimation *fadeAnim = [CABasicAnimation animationWithKeyPath:#"opacity"];
fadeAnim.fromValue = [NSNumber numberWithFloat:1.0];
fadeAnim.toValue = [NSNumber numberWithFloat:0.2];
fadeAnim.duration = 1.0;
fadeAnim.autoreverses = YES;
fadeAnim.repeatCount = INFINITY;
[colourbutton.titleLabel.layer addAnimation:fadeAnim forKey:#"opacity"];
I have a button which when pressed is meant to stop the animation.
-(IBAction)stopAnim {
[colourbutton.titleLabel.layer removeAllAnimations];
}
It works fine but one thing I am noticing is that is stops the animation suddenly, it doesn't let the animation finish. So how can I get it to finish the current animation and then stop. (Or in other words how can I get it to removeAllAnimations....withAnimation?).
On a side note, do I need to include CoreAnimation framework for this to work. So far the animation is running and I havn't imported the CoreAnimation framework.
Thanks, Dan.
Just add another animation and after that remove the first one like this:
CABasicAnimation *endAnimation = [CABasicAnimation animationWithKeyPath:#"opacity"];
endAnimation.fromValue = #(((CALayer *)colourbutton.titleLabel.layer.presentationLayer).opacity);
endAnimation.toValue = #(1);
endAnimation.duration = 1.0;
[colourbutton.titleLabel.layer addAnimation:endAnimation forKey:#"end"];
[colourbutton.titleLabel.layer removeAnimationForKey:#"opacity"];
The key here is to use the presentation layer to get the current state. Don't forget to set the actual end state of the layer, because the animation will be removed on completion.
In NKorotov's answer, he uses the presentationLayer to find out where you are in the animation. That is the correct way to go.
You could go with this solution, although IMO you would also have to calculate the duration animation correctly (based on the duration of the original animation and on how far you are along the animation path currently).
If you find it "silly" to add a new animation, you could perhaps call removeAllAnimations using dispatch_after at the correct time.

Animating CALayer opacity with pan gesture is not smooth

I have a CAGradientLayer overlaid on a CALayer. I want to animate it's opacity as I pan around the view they reside in.
So I simply have a CAGradientLayer instantiated like so:
this is how I instantiate the layer:
CAGradientLayer * gLayer = [CAGradientLayer layer];
NSArray * stops;
NSNumber *stopOne = [NSNumber numberWithFloat:0.0];
NSNumber *stopTwo = [NSNumber numberWithFloat:1.0];
[gLayer setColors:#[(id)clearColor.CGColor,(id)darkColor.CGColor]];
[gLayer setLocations:stops];
Then I change the opacity during the gesture:
gLayer.opacity = (here I put the variable that changes between 0 - 1 as the pan changes)
So, even though this works, the change is not smooth because I am not animating it I guess.
How can I animate the opacity to be a fluid change? I guess the catch is that the animation has to be fast so that it follows the pan gesture change without lagging.
Any suggestion?
Thank you
The change will be fluid if you make an appropriate small change on every change in the gesture. The problem, in fact, is just the opposite of what you suspect: it is that the opacity is being animated, implicitly, but you keep canceling that animation because you do it again and again. Thus the change in opacity has no chance to become visible until the whole gesture is over. The solution, therefore, is to prevent the animation.
So each time your gesture recognizer handler is called, if the state is UIGestureRecognizerStateChanged, you are going to turn off implicit animation and then change the opacity of the layer, like this:
[CATransaction setDisableActions:YES];
gLayer.opacity = whatever;
Assuming that each whatever value is only slightly different from the previous whatever value, the result will be smooth. (But of course it is up to you to supply a sensible whatever value each time. I do not know whether you're doing that, as you have not revealed that part of your code.)

UIView rotation and Subview (Button) touch

I have a UIView and on this UIView I have 8 buttons. I need to rotate this UIVIew every time So I am using the code below:
CABasicAnimation *fullRotation = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
fullRotation.fromValue = [NSNumber numberWithFloat:0];
fullRotation.toValue = [NSNumber numberWithFloat:((360*M_PI)/180)];
fullRotation.duration = 30;
fullRotation.repeatCount = MAXFLOAT;
[self.menuView.layer addAnimation:fullRotation forKey:#"360"];
It rotates my view properly, But I am unable to get touch on rotating buttons. Since the Parent view is rotating and the Buttons will also rotate, Touch isn't working. Thanks
Because you rotate layer. I think you can't do what you need, but try to use CGAffineTransform
CGAffineTransform transform //desired transform
[UIView animateWithDuration:time
animations:^{menuView.transform = transform;}];
and call it by timer.
Normal view touches do not work correctly during an animation. Animation only makes the views look like they are moving. Their actual coordinates don't move.
If you want to support tapping on objects you will need to roll your own tap handling code at the superview level using the presentation layer's hitTest method.
I have a sample project on github that shows how to do that:
iOS Core Animation demo

How to make scaling to persist with CABasicAnimation

I am creating some animation on my application and the code below zooms out an object till it disappears. I can't figure out how to make the object to disappear and keep that way, ie. how to make the animation stay put after it finishes. Any gotchas on that? Cheers!
CABasicAnimation* zoomOut = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
zoomOut.duration = 1;
zoomOut.toValue = [NSNumber numberWithFloat:0];
[draggedObject addAnimation:zoomOut forKey:nil];
I found it. It also needs the two methods below:
zoomOut.removedOnCompletion = NO;
zoomOut.fillMode = kCAFillModeForwards;
Ok so this happens because the animation doesn't actually change the underlying property, which is why it jumps back after the animation is complete.
Try adding this line before the line starting the animation -
zoomOut.removedOnCompletion = NO;

Resources