How to change a CALayer's default animation time? - iphone

I use these code to move a CALayer:
CGPoint position = self.position;
position.x += 10;
position.y += 10;
self.position = position;
There is a default animation when i change the position of a layer without any animation call.This will cause lag on movement of a layer when i use these code in touchesMoved:withEvent:.
What i want to do is remove the default animation, and move the layer immediately so that it can responds to touch event quickly, just like the "center" property of UIView does.
What should i do? special thx!

The other poster had the right idea, but didn't provide enough information.
If you want to change layer properties instantly, without animation, you need to put those changes inside a CATransaction where you disable actions:
[CATransaction begin];
//[CATransaction setAnimationDuration: 1.0/30.0];
[CATransaction setDisableActions: TRUE];
//Put layer changes you want to take place without animation here.
[CATransaction commit];

Simply set
[CATransaction setDisableActions:YES];
this will make the Layer move without lag

Related

Rotate in z-axis like core-animation slot machine

I was to able to move or animate my UIView by this code down here:
- (void) makeAnim1{
//downward animation
[UIView animateWithDuration:1.5
delay:0.15
options: UIViewAnimationCurveLinear
animations:^{
carousel.frame = CGRectOffset(carousel.frame, 0, 650);
}
completion:^(BOOL finished){ //task after an animation ends
[self performSelector:#selector(makeAnim1_1) withObject:nil afterDelay:2.0];
NSLog(#"Done!");
}];
}
- (void) makeAnim1_1{
//upward animation
[UIView animateWithDuration:1.5
delay:0.1
options: UIViewAnimationCurveLinear
animations:^{
carousel.frame = CGRectOffset(carousel.frame, 0, -650);
}
completion:^(BOOL finished){
NSLog(#"Done!");
}];
}
But it only move the UIView up and down. How can I make it spin like a Slot machine but only contains one image or view. Like rotate in z axis. But make it look like it contains more than one image.
Thanks for the help.
Instead of changing the frame inside the animation block you change the transform. The transform can be used to scale, rotate and translate (move) the view. You can only rotate around the z-axis but that is what you asked for. The transform property on the view takes a CGAffineTransform, like this:
// rotate pi/2 degrees clockwise
carousel.transform = CGAffineTransformMakeRotation(M_PI_2);
If you need to do more advanced transforms like rotating around another axis then you would need to use a little bit of Core Animation and to set the transform property of the views layer (which takes a CATransform3D instead of a CGAffineTransform).
As with all Core Animation code you need to import QuartzCore.framework and include QuartzCore/QuartzCore.h in your code.
The above animations you are doing is UIView animations which are only meant to animate views but the animation you are asking for requires more advanced animations of the views layer. I suggest that you look at the documentation for CABasicAnimation and also take a look at the Core Animation Programming Guide for iOS to learn more.
You can animate the x rotation of a views layer like this:
CABasicAnimation *slotAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.x"];
[slotAnimation setToValue:[NSNumber numberWithFloat:M_PI_2]];
// animation customizations like duration and timing
// that you can read about in the documentation
[[carousel layer] addAnimation:slotAnimation forKey:#"mySlotAnimation"];
The above code will indeed rotate the view around the x axis but will look very silly without perspective (search SO for "perspective Core Animation", it has been asked about before). There is probably a lot of tweaking to get the correct look but this should be enough to get you started.

The scope of a CATransaction block

I have seen some code that removes a given layer from it's superlayer by doing the following:
void RemoveImmediately(CALayer *layer) {
[CATransaction flush];
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue
forKey:kCATransactionDisableActions];
[layer removeFromSuperlayer];
[CATransaction commit];
}
I have written a method that changes the position of a given layer in an animated way using CAKeyframeAnimation, which looks like this:
- (void)animateMovingObject:(NXUIObject*)obj
fromPosition:(CGPoint)startPosition
toPosition:(CGPoint)endPosition
duration:(NSTimeInterval)duration {
CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
pathAnimation.calculationMode = kCAAnimationPaced;
pathAnimation.duration = duration;
CGMutablePathRef curvedPath = CGPathCreateMutable();
CGPathMoveToPoint(curvedPath, NULL, startPosition.x, startPosition.y);
CGPathAddCurveToPoint(curvedPath, NULL,
startPosition.x, endPosition.y,
startPosition.x, endPosition.y,
endPosition.x, endPosition.y);
pathAnimation.path = curvedPath;
[obj addAnimation:pathAnimation forKey:#"pathAnimation"];
CGPathRelease(curvedPath);
}
Up to know, everything is fine. Now suppose I have in my app a 'master layer', which has 3 sublayers. I want the first two sublayers to be moved to another position and the last one to be removed. So I did the following:
CALayer obj1 = ... // set up layer and add as sublayer
[self.masterLayer addSublayer:obj1];
[self animateMovingObject:obj1
fromPosition:CGPointMake(0.0, 0.0)
toPosition:CGPointMake(100.0, 100.0)
duration:2.0];
CALayer obj2 = ... // set up layer and add as sublayer
[self.masterLayer addSublayer:obj2];
[self animateMovingObject:obj2
fromPosition:CGPointMake(0.0, 0.0)
toPosition:CGPointMake(150.0, 100.0)
duration:2.0];
CALayer obj3 = ... // set up layer and add as sublayer
[self.masterLayer addSublayer:obj3];
// ...
RemoveImmediately(obj3); // This removes the two previous animations too
//[obj3 removeFromSuperlayer]; // This just removes obj3, the previous animations works
Note that if I call RemoveImmediately(), even passing obj3 as argument I can't see the first two layers being animated. However, if I remove the obj3 just by calling removeFromSuperlayer the first two layers are animated normally.
Looks like the CATransaction block cancels all animations being done, even those that were created with CAKeyframeAnimation.
What I'm I missing?
Thanks In advance.
The scope of a CATransaction should just be between the [CATransaction begin] and [CATransaction commit] lines.
Also, you shouldn't need the [CATransaction flush] in that code. Apple generally recommends you not force the execution of pending transactions for performance reasons, and the transaction you're setting up there should not affect any pending animations.
I'm guessing this odd behavior is still related to what you asked in your previous question, where the three animations are somehow interfering with one another. As I commented there, this is behaving like the three animations are being added to one layer, not three independent ones. I'd check and make sure there was no possibility of that happening (crossed pointers, etc.).

CATransaction: Layer Changes But Does Not Animate

I'm trying to animate part of UI in an iPad app when the user taps a button. I have this code in my action method. It works in the sense that the UI changes how I expect but it does not animate the changes. It simply immediately changes. I must be missing something:
- (IBAction)someAction:(id)sender {
UIViewController *aViewController = <# Get an existing UIViewController #>;
UIView *viewToAnimate = aViewController.view;
CALayer *layerToAnimate = viewToAnimate.layer;
[CATransaction begin];
[CATransaction setAnimationDuration:1.0f];
CATransform3D rotateTransform = CATransform3DMakeRotation(0.3, 0, 0, 1);
CATransform3D scaleTransform = CATransform3DMakeScale(0.10, 0.10, 0.10);
CATransform3D positionTransform = CATransform3DMakeTranslation(24, 423, 0);
CATransform3D combinedTransform = CATransform3DConcat(rotateTransform, scaleTransform);
combinedTransform = CATransform3DConcat(combinedTransform, positionTransform);
layerToAnimate.transform = combinedTransform;
[CATransaction commit];
// rest of method...
}
I've tried simplifying the animation to just change the opacity (for example) and it still will not animate. The opacity just changes instantly. That leads me to believe something is not setup properly.
Any clues would be helpful!
Animations on the root layer of a view are disabled by default. Try applying a transform to the view instead, e.g. [view setTransform:CGTransform3D...]. If you must do it at the layer level, add a layer to the root layer and perform your transforms on it instead. Also the view animation block [UIView beginAnimations...] only has an effect when animating view properties--as opposed to layer properties.
Update:
So here is what your code would look like with explicit animation (CATransaction is not required)
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"transform"];
CATransform3D rotateTransform = CATransform3DMakeRotation(0.3, 0, 0, 1);
CATransform3D scaleTransform = CATransform3DMakeScale(0.10, 0.10, 0.10);
CATransform3D positionTransform = CATransform3DMakeTranslation(24, 423, 0);
CATransform3D combinedTransform = CATransform3DConcat(rotateTransform, scaleTransform);
combinedTransform = CATransform3DConcat(combinedTransform, positionTransform);
[anim setFromValue:[NSValue valueWithCATransform3D:CATransform3DIdentity]];
[anim setToValue:[NSValue valueWithCATransform3D:combinedTransform]];
[anim setDuration:1.0f];
[layerToAnimate addAnimation:anim forKey:nil];
Keep in mind that this only performs the animation. You actually have to set the transform property of the layer with a call to:
[layerToAnimate setTransform:combinedTransform];
as well. Otherwise it will just snap back to its starting position.
Layer properties are animated implicitly whenever you set them except in the case where you are animating the root layer of a view. In that case animations are turned off by default and I've found that what I always have to do instead is animate the view rather than the layer when I am interested in animating the root. So what this means is that a call to any layer within your layer tree that makes a property change will be animated implicitly. For example:
CALayer *root = [view layer];
CALayer *sublayer = [[root sublayers] objectAtIndex:0];
[sublayer setTransform:combinedTransform];
This is the only way I know (knew) you can actually use implicit animation on a layer. However, what your code has pointed out is that you can turn the layer animation on for the root layer simply by placing the changes to the layer within a UIView animation block. That's pretty interesting and handy. This is quite a helpful discovery.
Maybe this is buried in the docs somewhere, but I have yet to come across it.
Setting your layerToAnimate delegate to your rootView.layer works:
viewToAnimate.layer.delegate = rootView.layer;
Assuming you add the view/layer to the root view/layer as well. Cannot find where this changed, implicit animations use to just work.
It turns out you can turn the layer animation on for the root layer by setting its delegate property to nil. I don't know if it's good practice.

Drawing and Animating with UIView or CALayer?

i am trying to implement a graph which a uiview draws. There are 3 UIViews to animate a left/right slide. The problem is, that i can't cancel the UIView animation. So I replaced the UIViews by CALayer. Now, the question is if CALayer is appicable for this? Is it normal to draw on a CALayer like this? And why is a CALayer so slow when I manipulate the frame properties.
CGRect frame = curve.frame;
frame.origin.x -= diffX;
[curve setFrame:frame];
Is there a alternativ?
P.S. I am a german guy. Sorry for mistakes.
I got the animation with CATransaction, but now I will animate a x move with CABasicAnimation. That's no problem expect that the position of the layer go back to the previous x.
CABasicAnimation *theAnimation;
theAnimation = [CABasicAnimation animationWithKeyPath:#"position"];
theAnimation.delegate = self;
theAnimation.duration = 1.0;
theAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
CGPoint position = [[curves objectAtIndex:i]position];
position.x = [[curves objectAtIndex:i]position].x - diffX;
[theAnimation setToValue:[NSValue valueWithCGPoint:position]];
[[curves objectAtIndex:i] addAnimation:theAnimation forKey:[NSString stringWithFormat: #"translate.x.%d", index]];
The position changes the position (e.g. xStart = 100, xEnd = 200), but when the animation ends the layer goes back to the beginning x (e.g. x = 100).
Is that normal? How can I solve this problem, that the end position doesn't change anymore?
I tried to changed the removeOnComplete property but that didn't effect.
Hope for help.
Markus
Not sure what you mean by 'slow', but setting the frame of a CALayer in this way uses 'implicit animation'. That is, it will animated the transition from the old frame to the new frame. You can turn this off:
[CATransaction begin];
[CATransaction setValue: (id) kCFBooleanTrue forKey: kCATransactionDisableActions];
[curve setFrame:frame];
[CATransaction commit];
However, this is usually considered an advantage of CALayer. You way want to just use UIViews here, which will not, by default, animate transitions such as this.
Instead of setting the destination position in theAnimation, just set the position property of the thing you want to move.
When you addAnimation:theAnimation, you're setting the "visual style" of any changes to the keyPath property you specified.
When you change the position of the object that the animation is attached to, say from (0,0) to (500,500), CoreAnimation will animate the change for you. The theAnimation object doesn't need the start and end positions, since the underlying object has them.

Need help with animation on iPhone

I'm working on an animated clock application for the iPhone, and I have to pivot all the 3 nodes in the view, which I have obtained in the following code:
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
clockarm.layer.anchorPoint = CGPointMake(0.0, 0.0);
[CATransaction commit];
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanFalse forKey:kCATransactionDisableActions];
[CATransaction setValue:[NSNumber numberWithFloat:50.0] forKey:kCATransactionAnimationDuration];
CABasicAnimation *animation;
animation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
animation.fromValue = [NSNumber numberWithFloat:-60.0];
animation.toValue = [NSNumber numberWithFloat:2 * M_PI];
animation.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionLinear];
animation.delegate = self;
[clockarm.layer addAnimation:animation forKey:#"rotationAnimation"];
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[CATransaction commit];
The problem it's just rotating once, ie. only 360 degree and then stopping. I want to raotate the needles indefinitely. How would I do that?
I've never done it, but here's what I would try. You've already set the delegate to self. Implement the delegate's method animationDidStop:finished: and simply call the method where you'll put the code above. Make sure to call it on the main thread (using performOnMainThread).
This is related to the question asked here, where I pointed out my response to a similar question. In that case, I split the rotation animations into half-circle rotations, made them cumulative, and set the number of half rotations as the repeat count. As I suggest in both, doing this as a CAKeyframeAnimation might produce a cleaner rotation.
In your case, you could set the number of repetitions to a high enough value that the animation would never reach its end. As a fallback, you could follow François P.'s and Squeegy's suggestion and add a callback to restart the animation if it ever completes.
Add the animationDidFinish delegate method and setup the animation again for another rotation. CA wont let you turn things more than 360 degrees.
I would look at the problem slightly differently. I'm not sure why you're using 50 seconds for the time, but I wouldn't rely on this to keep accurate time. I would instead set a timer which fires every second, or half second, and animates the clock to move from it's current position to the new time.
P.S. Your fromValue looks to be in degrees, but it should be radians.
I use "scalingAnimation.repeatCount=1000000000;" for the purpose. Not sure if it's the way for you.
for n no. of repetition try to use value -1, i never do this but may it will help u.