How to animate an image in backward using CAKeyFrameAnimation - ios

I'm new in iOS development. I'm creating a Keyframe animation for rotating the image.The image was successfully rotated in forward.I want to rotate the image in full and full backward.
This is my code.
UIImageView *tempView = [[UIImageView alloc] initWithFrame:CGRectMake(28,158,133,133)];
tempView.image = [UIImage imageNamed:#"1_03.png"];
[self.view addSubview:tempView];
const NSUInteger rotations = 1;
const NSTimeInterval duration = 4.0f;
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:#"transform.rotation"];
CGFloat touchUpStartAngle = 0;
CGFloat touchUpEndAngle = (M_PI);
CGFloat angularVelocity = (((2 * M_PI) * rotations) + M_PI) / duration;
anim.values = #[#(touchUpStartAngle), #(touchUpStartAngle + angularVelocity * duration)];
anim.duration = duration;
anim.delegate = self;
anim.repeatCount = INFINITY;
[tempView.layer addAnimation:anim forKey:#"animation"];
tempView.transform = CGAffineTransformMakeRotation(touchUpStartAngle + (touchUpEndAngle));
How to do this.

Try this.
UIImageView *tempView = [[UIImageView alloc] initWithFrame:CGRectMake(28,158,133,133)];
tempView.image = [UIImage imageNamed:#"1_03.png"];
[self.view addSubview:tempView];
const NSUInteger rotations = 1;
const NSTimeInterval duration = 4.0f;
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:#"transform.rotation"];
CGFloat touchUpStartAngle = 0;
CGFloat touchUpEndAngle = (M_PI);
CGFloat angularVelocity = (((2 * M_PI) * rotations) + M_PI) / duration;
anim.values = #[#(touchUpStartAngle), #(touchUpStartAngle - angularVelocity * duration)];
anim.duration = duration;
anim.delegate = self;
anim.repeatCount = INFINITY;
[tempView.layer addAnimation:anim forKey:#"animation"];
tempView.transform = CGAffineTransformMakeRotation(touchUpStartAngle + (touchUpEndAngle));

Try this
CGFloat touchUpStartAngle = 0;
CGFloat touchUpEndAngle = (M_PI);
CGFloat angularVelocity = (((2 * M_PI) * rotations) + M_PI) / duration;
anim.values = #[#(touchUpStartAngle), #(touchUpStartAngle - angularVelocity * duration)];

Related

Suspend Particles in CAEmitterLayer

I have the following code that adds particles to a UIView named ParentView at the center of some other UIView:
CAEmitterLayer *emitterLayer = [CAEmitterLayer layer];
emitterLayer.emitterPosition = CGPointMake(view.center.x, view.center.y - view.frame.size.height / 3);
emitterLayer.emitterZPosition = 10;
emitterLayer.emitterSize = CGSizeMake(view.bounds.size.width, 0);
emitterLayer.emitterShape = kCAEmitterLayerSphere;
CAEmitterCell *emitterCell = [CAEmitterCell emitterCell];
emitterCell.scale = 0.1;
emitterCell.scaleRange = 0.2;
emitterCell.emissionRange = 45;
emitterCell.lifetime = 0.75;
emitterCell.birthRate = 60;
emitterCell.velocity = 200;
emitterCell.velocityRange = 50;
emitterCell.yAcceleration = 250;
emitterCell.contents = (id)[[UIImage imageNamed:#"particle.png"] CGImage];
emitterLayer.emitterCells = [NSArray arrayWithObject:emitterCell];
[parentView.layer addSublayer:emitterLayer];
Everything works well but now I want to pause or suspend the animation, so the particles "freeze". Can this be done?
I think pause the layer will work,
-(void)pauseLayer:(CALayer*)layer
{
CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
layer.speed = 0.0;
layer.timeOffset = pausedTime;
}

CALayer animate contents between more than 2 images impossible?

Does anyone has managed to animate the contents property of a CALayer between 3 images with successful interpolation. It seems that the animation is unable to manage interpolation between more than two images. My CGImages have all the same size.
CAKeyframeAnimation * animation =
[CAKeyframeAnimation animationWithKeyPath:#"contents"];
animation.calculationMode = kCAAnimationLinear;
animation.beginTime = AVCoreAnimationBeginTimeAtZero;
animation.duration = duration;
animation.fillMode = kCAFillModeBoth;
animation.removedOnCompletion = NO;
CGFloat width1 = CGImageGetWidth(image1.CGImage);
CGFloat height1 = CGImageGetHeight(image1.CGImage);
CGFloat width2 = CGImageGetWidth(image2.CGImage);
CGFloat height2 = CGImageGetHeight(image2.CGImage);
CGFloat width3 = CGImageGetWidth(image3.CGImage);
CGFloat height3 = CGImageGetHeight(image3.CGImage);
NSLog(#"first: W:%f H:%f, second: W:%f H:%f, third: W:%f H:%f",width1,height1,width2,height2,width3,height3);
animation.keyTimes = #[#0.0,#0.1,#0.3,#0.6,#1.0];
animation.values =
#[(id)image1.CGImage,
(id)image1.CGImage,
(id)image2.CGImage,
(id)image2.CGImage,
(id)image3.CGImage];
THE Log
2014-01-22 16:05:24.985 myApp[8527:60b] first: W:2592.000000 H:1936.000000, second: W:2592.000000 H:1936.000000, third: W:2592.000000 H:1936.000000

CABasicAnimation lags (iOS5)

i got some simple animation that works perfect on iOS 6, but on iOS5 its lagging and often freezes screen without any reason
i need help really, because i dont have any ideas what could be wrong here. By the way i tried to turn off each of the animations, and looks like the problem is in "transform" animation
here is the code:
-(void)drawDotAtPointAnimated:(CGPoint)p withRadius:(CGFloat)radius andColor:(UIColor *)color
{
CGRect circleFrame = CGRectMake(p.x - radius*self.scale, p.y - radius*self.scale, radius*2*self.scale, radius*2*self.scale);
CGPoint circleAnchor = CGPointMake(0.5, 0.5);
NSLog(#"animating dot with x = %f and y = %f", p.x, p.y);
NSLog(#"=====mid x = %f, mid y = %f", CGRectGetMidX(circleFrame), CGRectGetMidY(circleFrame));
NSLog(#"=====max x = %f, max y = %f", CGRectGetMaxX(circleFrame), CGRectGetMaxY(circleFrame));
self.shapeLayer = [CAShapeLayer layer];
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, circleFrame.size.width, circleFrame.size.height)];
self.shapeLayer.path = path.CGPath;
self.shapeLayer.anchorPoint = circleAnchor;
self.shapeLayer.frame = circleFrame;
self.shapeLayer.fillColor = color.CGColor;
[self.layer addSublayer:self.shapeLayer];
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:#"transform"];
animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.0, 0.0, 0)];
animation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(4.0, 4.0, 1.0)];
if(self.mode == GameModeOffline) {
animation.repeatCount = OFFLINE_GAME_DOT_ANIMATION_REPEAT_COUNT;
} else {
animation.repeatCount = ONLINE_GAME_DOT_ANIMATION_REPEAT_COUNT;
}
animation.duration = 1;
animation.delegate = self;
[self.shapeLayer addAnimation:animation forKey:#"transform"];
CABasicAnimation* fadeAnim = [CABasicAnimation animationWithKeyPath:#"opacity"];
fadeAnim.fromValue = [NSNumber numberWithFloat:1.0];
fadeAnim.toValue = [NSNumber numberWithFloat:0.0];
fadeAnim.duration = 1.0;
if(self.mode == GameModeOffline) {
fadeAnim.repeatCount = OFFLINE_GAME_DOT_ANIMATION_REPEAT_COUNT;
} else {
fadeAnim.repeatCount = ONLINE_GAME_DOT_ANIMATION_REPEAT_COUNT;
}
[self.shapeLayer addAnimation:fadeAnim forKey:#"opacity"];
self.shapeLayer.transform = CATransform3DMakeScale(4.0, 4.0, 1);
self.shapeLayer.opacity = 0.0;
any help would be appreciated
Scale by zero at start? Don't you want to scale by {1, 1, 1} ? (ie. identity transform)
(It seems this has simply solved the problem, so I'm posting my comment as an answer).

Changing the speed of a CAKeyframeAnimation mid-animation in iOS

I am trying to allow the user to change the speed of my animation. I am making a CAKeyframeAnimation with a bezier path, and I can get it to display and run correctly.
I try to change the speed by creating a new animation path with a different duration. The planes go back to the beginning (which I haven't tried to fix yet) and do speed up. The path that they are being drawn on goes away at the time it would have had the animation never changed speeds. When the planes finish, another appears at the point at which the animation was paused in the first place. I have no idea what I'm doing wrong. My question is similar to this one modifying dynamically the duration of CAKeyframeAnimation, but I don't understand what the OP said about finally using blocks.
//The first two methods are in a class subclassing UIView
/** Pause each plane's animation */
- (void)pauseAnimation
{
CFTimeInterval pausedTime = [[self layer] convertTime:CACurrentMediaTime() fromLayer:nil];
[self layer].speed = 0.0;
[self layer].timeOffset = pausedTime;
}
/** Resume each plane's animation */
- (void)resumeAnimation
{
CFTimeInterval pausedTime = [[self layer] timeOffset];
[self layer].speed = 1.0;
[self layer].timeOffset = 0.0;
CFTimeInterval timeSincePause = [[self layer] convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
for(SEPlane *plane in planes){
plane.planeAnimationPath.speedMultiplier = 5;
[plane.planeAnimationPath beginAnimation:self];
}
//[self layer].beginTime = timeSincePause;
}
//This method is in the class of planeAnimationPath
/** Begin animating plane along given path */
- (void)beginAnimation:(UIView *) view
{
planeAnimation = nil;
// Create animation layer for animating plane
planeAnimation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
planeAnimation.path = [bezierPath CGPath];
planeAnimation.duration = approximateLength/(ANIMATION_SPEED * self.speedMultiplier);
planeAnimation.calculationMode = kCAAnimationPaced;
planeAnimation.fillMode = kCAFillModeForwards;
planeAnimation.rotationMode = kCAAnimationRotateAuto;
planeAnimation.removedOnCompletion = YES;
[planeAnimation setDelegate:self];
// Add animation to image-layer
[imageLayer addAnimation:planeAnimation forKey:animationKey];
// Add image-layer to view
[[view layer] addSublayer:imageLayer];
}
Unlike default animations, which animate from the current position to the target position, CAKeyframeAnimations don't (as far as I can tell). Besides how would you interpret an animation where the current position is not on the path?
The simplest option that I can think of is to do the following in the setter of speedMultiplier:
Create a new animation with the desired path.
Set the duration as if speedMultiplier was 1
Set the speed to speedMultiplier
Set the timeOffset to duration * "percent of new animation already complete"
Add the animation to the layer.
As you might have guessed, the tricky part is step 4. For simple paths this is easy but for arbitrary paths, it will get a bit more complicated. As a starting point, you will need the formula for bezier quadratic and cubic curves. Search for "distance parametrization of bezier curves" and you will find tons of stuff.
Here is a code sample for a simple rectangular path. The window simply has a MPView and a slider:
#implementation MPView {
IBOutlet NSSlider *_slider; // Min=0.0, Max=5.0
CALayer *_hostLayer;
CALayer *_ballLayer;
CAKeyframeAnimation *_ballPositionAnimation;
double _speed;
}
- (void) awakeFromNib
{
CGRect bounds = self.bounds;
[CATransaction begin];
[CATransaction setDisableActions:YES];
_speed = 1.0;
_hostLayer = [CALayer layer];
_hostLayer.backgroundColor = CGColorGetConstantColor(kCGColorBlack);
self.layer = _hostLayer;
self.wantsLayer = YES;
_ballLayer = [CALayer layer];
_ballLayer.bounds = CGRectMake(0, 0, 32, 32);
_ballLayer.position = CGPointMake(40, 40);
_ballLayer.backgroundColor = CGColorGetConstantColor(kCGColorWhite);
_ballLayer.cornerRadius = 16;
_hostLayer.sublayers = #[_ballLayer];
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, _ballLayer.position.x, _ballLayer.position.y);
CGPathAddRect(path, NULL, CGRectInset(bounds, 40, 40));
CGPathCloseSubpath(path);
_ballPositionAnimation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
_ballPositionAnimation.path = path;
_ballPositionAnimation.duration = 6;
_ballPositionAnimation.repeatCount = HUGE_VALF;
CGPathRelease(path);
[_ballLayer addAnimation:_ballPositionAnimation forKey:_ballPositionAnimation.keyPath];
[CATransaction commit];
[_slider bind:NSValueBinding toObject:self withKeyPath:#"speed" options:#{NSContinuouslyUpdatesValueBindingOption:#YES}];
}
- (double) speed
{
return _speed;
}
- (void) setSpeed:(double)speed
{
_speed = speed;
CGPoint pos = [(CALayer*)_ballLayer.presentationLayer position];
[CATransaction begin];
[CATransaction setDisableActions:YES];
_ballPositionAnimation.speed = _speed;
_ballPositionAnimation.duration = 5.0;
_ballPositionAnimation.timeOffset = _ballPositionAnimation.duration * [self percentOfPathCompleted:pos];
[_ballLayer addAnimation:_ballPositionAnimation forKey:_ballPositionAnimation.keyPath];
[CATransaction commit];
}
- (double) percentOfPathCompleted:(CGPoint)p
{
CGRect rect = CGRectInset(self.bounds, 40, 40);
double minX = NSMinX(rect);
double minY = NSMinY(rect);
double maxX = NSMaxX(rect);
double maxY = NSMaxY(rect);
double offset = 0.0;
if (p.x == minX && p.y == minY)
return 0.0;
else if (p.x > minX && p.y == minY)
offset = (p.x - minX) / rect.size.width * 0.25;
else if (p.x == maxX && p.y < maxY)
offset = (p.y - minY) / rect.size.height * 0.25 + 0.25;
else if (p.x > minX && p.y == maxY)
offset = (1.0 - (p.x - minX) / rect.size.width) * 0.25 + 0.50;
else
offset = (1.0 - (p.y - minY) / rect.size.height) * 0.25 + 0.75;
NSLog(#"Offset = %f",offset);
return offset;
}
#end

Animating a UIView to a value without a specified duration

I want to animate a sub-classed UIView using Core Animation. The oddity in my case is that I want the animation to run a fixed amount every frame and not by the duration. So animating from 100 -> 200 should take longer than 100 -> 50, but the "velocity" of the view should be constant. My code currently looks like this:
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:#"transform"];
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
animation.duration = 0.4;
[[self layer] addAnimation:animation forKey:#"transform"];
self.transform = CGAffineTransformMakeTranslation(0, 100);
It this possible? How would it be done?
Why not just compute the desired time from the desired movement? Something like:
CGFloat distanceToTimeFactor = 0.1;
CGPoint current = self.center;
CGPoint new = CGPointMake(100,100);
CGFloat xDist = (new.x - current.x);
CGFloat yDist = (new.y - current.y);
CGFloat distance = sqrt((xDist * xDist) + (yDist * yDist));
CGFloat duration = distance * distanceToTimeFactor;
And then animate with that duration.

Resources