Control speed in CABasicAnimation - ios

I am newbie to iOS app development. I am using CABasicAnimation to draw a line horizontally across my app screen. I am successfully able to draw the line however I am not able to control the speed of animation.
Below is the code for drawing line.
-(void)drawLine{
_boxPath = [UIBezierPath bezierPath];
[_boxPath moveToPoint:CGPointMake(0.0,60.0)];
[_boxPath addLineToPoint:CGPointMake(self.view.bounds.size.width/2, 60.0)];
CAShapeLayer *layer = [CAShapeLayer layer];
layer.frame = self.view.bounds;
layer.strokeColor = [[UIColor redColor] CGColor];
layer.fillColor = [[UIColor blueColor] CGColor];
layer.lineWidth = 5.0f;
layer.lineJoin = kCALineJoinBevel;
layer.path = _boxPath.CGPath;
layer.speed = 3.0;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"line"];
animation.duration = 3.0;
[self.view.layer addSublayer:layer];
[layer addAnimation:animation forKey:#"line"];
}
No matter whatever values I change for layer speed and animation duration theres no change in animation speed. I want to slow the speed by which the line is drawn.
Any suggestions would be of great help

Your code is nonsense. Three points:
If you're going to use implicit animation, you would use CATransaction setAnimationDuration: to set a longer duration.
If you're going to use explicit animation, use it correctly. Creating a CABasicAnimation and just adding it does nothing; you need to configure it first. There is no "line" key path so your animation is meaningless.
You cannot animate nothing. You have to animate a change in something. What you probably want to animate is a change in the strokeEnd from zero to one.

Related

How to animate drawing part of CGPath

I want to approach the following scenario:
If I draw a rectangle using CGBezierPath and CAShapeLayer like the following:
CAShapeLayer *layerX = [CAShapeLayer layer];
layerX.path = path.CGPath;
layerX.lineWidth = 3;
layerX.strokeColor = [[UIColor whiteColor] CGColor];
layerX.fillColor = [[UIColor clearColor] CGColor];
[self.view.layer addSublayer: layerX];
And I want to add animation like the image I attached, this cleared area keeps moving around the rectangle path so it gives visual effect of the rectangle being drawn over and over. [Snake movement in old snake games]
I tried animating using CABasicAnimation but I literally couldn't achieve anything, thanks in advance.
Try this code may be helpful.
UIBezierPath *drawablePath = [UIBezierPath bezierPathWithRect:CGRectMake(100.0, 100.0, 150.0, 100.0)];
CAShapeLayer *squareLayer = [[CAShapeLayer alloc] init];
squareLayer.path = drawablePath.CGPath;
squareLayer.strokeColor = [UIColor redColor].CGColor;
squareLayer.lineWidth = 5.f;
squareLayer.fillColor = [UIColor clearColor].CGColor;
squareLayer.strokeEnd = 1.f;
//squareLayer.strokeEnd = 0.9; // this line use draw half line but some animation issue.
[self.view.layer addSublayer:squareLayer];
CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
drawAnimation.fromValue = (id)[NSNumber numberWithFloat:0.10];
drawAnimation.toValue = (id)[NSNumber numberWithFloat:1.f];
drawAnimation.duration = 5.f;
drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[squareLayer addAnimation:drawAnimation forKey:#"drawRectStroke"];
This code is use to draw rectangle with animation.
You have to animate both: strokeStart and strokeEnd and use a CAAnimationGroup to make them happen at the same time.
I found this link which does something similar but in swift. I think it might help.
I will try to implement it if you can't do it. But I can't right now.
Happy coding.
let drawablePath = UIBezierPath(rect: CGRect(x: 100, y: 100, width: 150, height: 150))
let squareLayer = CAShapeLayer()
squareLayer.path = drawablePath.cgPath;
squareLayer.strokeColor = UIColor.red.cgColor;
squareLayer.lineWidth = 5;
squareLayer.fillColor = UIColor.clear.cgColor;
squareLayer.strokeEnd = 1;
//squareLayer.strokeEnd = 0.9; // this line use draw half line but some animation issue.
flowerView.layer.addSublayer(squareLayer)
let drawAnimation = CABasicAnimation(keyPath: "strokeEnd")
drawAnimation.fromValue = 0.1
drawAnimation.toValue = 1.0
drawAnimation.duration = 5
drawAnimation.timingFunction = CAMediaTimingFunction(name:.linear)
squareLayer.add(drawAnimation, forKey:"drawRectStroke")
Not really an answer to the original question but here's a Swift 5 version of Dixit Akabari's answer, which was useful in itself in that it demonstrates how to animate drawing a UIBezierPath by animating strokeEnd.

Adding scale animation for CAShapeLayer

I'd like to implement transition animation between two UIView according to the material design as shown on screenshot below (first part of animation only):
But I'd like to make it more realistic with applying animated mask to UIView. Here is a code I've developed:
- (IBAction)onButtonTapped:(UIButton *)sender
{
// 1. make a hole in self.view to make visible another view
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRect:self.view.bounds];
[maskPath appendPath:[UIBezierPath bezierPathWithArcCenter:sender.center
radius:sender.frame.size.width/2.0f
startAngle:0.0f
endAngle:2.0f*M_PI
clockwise:NO]];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.fillRule = kCAFillRuleEvenOdd;
maskLayer.fillColor = [UIColor blackColor].CGColor;
maskLayer.path = maskPath.CGPath;
// 2. add scale animation for resizing hole
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.fromValue = [NSValue valueWithCGRect:sender.frame];
animation.toValue = [NSValue valueWithCGRect:self.view.bounds];
animation.duration = 3.0f;
[maskLayer addAnimation:animation forKey:#"scaleAnimation"];
self.view.layer.mask = maskLayer;
}
My idea is adding a "hole" into modal UIView and then scale wit animation.
The problem is that it doesn't work properly. First part of code make a hole well. But scaling animation of a hole doesn't work at all.
You can't add an animation to a layer if the layer isn't in some window's layer tree. You need to do things in this order:
self.view.layer.mask = maskLayer;
[CATransaction flush];
[maskLayer addAnimation:animation forKey:#"scaleAnimation"];

CAGradientLayer issues for animated circle created using CAShapeLayer

I am drawing an animated circle using the UIBezierPath and CAShapeLayer. Now while adding the gradient effect the problem occurring is - CAGradientLayer adds it's gradient effect from upside down. But I want gradient effect from the starting point of the circle to the end point of the circle.
What I am getting is -
My code is - (it's a function)
circle.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(radius+6,radius+6) radius:radius startAngle:DEGREES_TO_RADIANS(startAngle) endAngle:DEGREES_TO_RADIANS(endAngle) clockwise:isClockWise].CGPath;
//circle.position = CGPointMake(CGRectGetMidX(self.frame)-radius, CGRectGetMidY(self.frame)-radius);
circle.fillColor = fillColor.CGColor;
circle.strokeColor = strokeColor.CGColor;
circle.lineWidth = lineWidth;
circle.strokeEnd = percentToDraw/100;
circle.lineCap = kCALineCapRound;
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.startPoint = CGPointMake(0.5,1.0);
gradientLayer.endPoint = CGPointMake(0.5,0.0);
gradientLayer.frame = CGRectMake(0, 0, self.frame.size.width , self.frame.size.height);
NSMutableArray *colors = [NSMutableArray array];
[colors addObject:(id)[UIColor blackColor].CGColor];
[colors addObject:(id)strokeColor.CGColor];
gradientLayer.colors = colors;
[gradientLayer setMask:circle];
[self.layer addSublayer:gradientLayer];
CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
drawAnimation.duration = duration;
drawAnimation.repeatCount = 1.0;
drawAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
drawAnimation.toValue = [NSNumber numberWithFloat:percentToDraw/100];
drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
[circle addAnimation:drawAnimation forKey:#"drawCircleAnimation"];
How do I add gradient so that it starts(with red) and goes forward to finish point(with black). ?
Assuming you are talking about a conical/angle gradient, then there is no system provided method for achieving that with CAGradientLayer. There are libraries such as AngleGradientLayer that try to provide this functionality, however, as with any 3rd party solution, it should be used with caution.
You can also look at approaches like ones discussed here or here, however they would require you to slightly refactor your logic.
Note that this suggestions would only work in a circular case.

CABasicAnimation Circular Mask Reveal Scaling Path Animation

I am aiming to reveal a menu with a circular reveal scaling animation. For this I first set the mask to a radius with a radius of 0 at the beginning.
#define DEGREES_TO_RADIANS(angle) ((angle) / 180.0 * M_PI)
CAShapeLayer *mask = [CAShapeLayer new];
mask.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(25, 25) radius:0 startAngle:0 endAngle:DEGREES_TO_RADIANS(360) clockwise:YES].CGPath;
self.layer.mask = mask;
Then I animate the new mask with a CABasicAnimation.
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"path"];
animation.toValue = (id)[UIBezierPath bezierPathWithArcCenter:CGPointMake(25, 25) radius:self.frame.size.height*1.2 startAngle:0 endAngle:DEGREES_TO_RADIANS(360) clockwise:YES].CGPath;
animation.removedOnCompletion = NO;
animation.duration = 0.45f;
animation.fillMode = kCAFillModeForwards;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
[self.layer.mask addAnimation:animation forKey:nil];
On the first look this is working but the problem is that the animation itself is more oval than circular: http://i.stack.imgur.com/fqPYJ.png
The code is inspired by Animate circular mask of UIImageView in iOS.
Your code looks reasonable. (Although if you're always using a full circle you could probably use the simpler bezierPathWithOvalInRect method instead of bezierPathWithArcCenter).
It's hard to tell what I'm looking at in the image you linked. The two layer are white so we can't tell what is what. You might want to set one of the layers to a colored background while you're testing.
As to why you would get an oval: Do you have a transform on any of your layers? Specifically a scale transform? That would cause your distortion.

Red Box Moving in Place of UIImage View in Core Animation

My first Core Animation is just an image moving from point a to b. For some reason when the animation is called a red box appears and moves in place of my image while it just sits there.
Here is my code:
-(IBAction)preform:(id)sender{
CALayer *layer = [CALayer layer];
[layer setPosition:CGPointMake(100.0, 100.0)];
[layer setBounds:CGRectMake(0.0, 0.0, 50.0, 60.0)];
[layer setBackgroundColor:[[UIColor redColor] CGColor]];
[imView.layer addSublayer:layer];
CGPoint point = CGPointMake(imView.frame.origin.x, imView.frame.origin.y);
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"position"];
anim.fromValue = [NSValue valueWithCGPoint:point];
anim.toValue = [NSValue valueWithCGPoint:CGPointMake(point.x + 50, point.y)];
anim.duration = 1.5f;
anim.repeatCount =1;
anim.removedOnCompletion = YES;
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
[layer addAnimation:anim forKey:#"position"];
}
If you don't need that red box for your app's UI, don't create it and instead apply the animation to the view's layer. If you do need that red box, do create, but apply the animation to the view's layer, not the red box layer
Also note that unless your change your layer's anchorPoint, the position property actually is the center of the layer, not the corner. You need to take that in account for making smooth animations.
Finally, if you don't want your view to snap back after the animation is done, you will need to set it's new position. Don't worry, while the animation is in play, the view's relocation isn't visible.
Suggested (and untested) code:
-(IBAction)preform:(id)sender{
CGPoint point = CGPointMake(imView.frame.size.width/2.0 , imView.frame.size.height/2.0 );
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"position"];
anim.fromValue = [NSValue valueWithCGPoint:point];
anim.toValue = [NSValue valueWithCGPoint:CGPointMake(point.x + 50, point.y)];
anim.duration = 1.5f;
anim.repeatCount =1;
anim.removedOnCompletion = YES;
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
[imView.layer addAnimation:anim forKey:#"position"];
imView.center = CGPointMake( imView.center.x + 50, imView.center.y
}
This would all be easier if you used the UIView animation helper functions instead, but I figure you are trying to learn how to use CAAnimation

Resources