Animate fillColor property - ios

I am trying to achieve the filling of a circle(something like a pie).
I want to fill it from 0 to 360
This is what i have done
CAShapeLayer *circleLayer=[CAShapeLayer layer];
circleLayer.path=[UIBezierPath bezierPathWithArcCenter:CGPointMake(150, 150) radius:100 startAngle:0 endAngle:360 clockwise:YES].CGPath;
circleLayer.strokeColor=[UIColor redColor].CGColor;
circleLayer.fillColor=[UIColor blueColor].CGColor;
[self.view.layer addSublayer:circleLayer];
//adding animation
CABasicAnimation *basicAnimation=[CABasicAnimation animationWithKeyPath:#"fillColor"];
basicAnimation.fillMode=kCAFillModeForwards;
basicAnimation.fromValue=(id)[UIColor clearColor].CGColor;
basicAnimation.toValue=(id)[UIColor yellowColor].CGColor;
basicAnimation.duration=1.5f;
basicAnimation.repeatCount=10;
basicAnimation.autoreverses=YES;
[circleLayer addAnimation:basicAnimation forKey:#"fillColor"];
I am not able to figure out,how do i acheive the effect of color filling from start angle to end angle.
Thanks

Your description of what you want to do is unclear. Are you trying to create a "clock wipe" effect, where the color change sweeps like the second hand of a clock?
If so, check out this demo app I created on github:
github animation demo project
Look in particular at the description of the Clock Wipe animation in the read me file.
Here is what the clock wipe animation looks like:
This project animates the mask of an image view to do a clock wipe reveal animation, but it would be a simple matter to animate a shape layer as a content layer of a view instead of making it a mask.

CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
animation.duration = 0.7f;
animation.fromValue = [NSNumber numberWithFloat:0.0f];
animation.toValue = [NSNumber numberWithFloat:1.0f];
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[circleLayer addAnimation:animation forKey:#"strokeEnd"];

I have implemented a shape layer subclass for similar requirements. Here is the Github link. For your requirement the arc start radius is 0.0f and end radius is the circle radius. You can also refer the sample project in that link.

Related

iOS: how to animate the fill in uibezier path?

I am working on a kids letter tracing app - see attached screen shot.
I was able to display the letter by using the bezierpath identified by the font glyphs and allow writing the touches inside the bezierpath.
Now, i want to add an help animation so that it shows how to write this letter from start to finish.
How to do that?
Consider we display "A" and i want to show animation shows how to write "A" for kids.
Any pointers / ideas.
thanks much.
I used this animation for my app. Created a circular progress bar that fills up dot clockwise
CABasicAnimation *pathAnim = [CABasicAnimation animationWithKeyPath: #"path"];
pathAnim.toValue = (id)newPath.CGPath;
// -- initialize CAAnimation group with duration of 0.2secs and beginTime will begin after another.
CAAnimationGroup *anims = [CAAnimationGroup animation];
anims.animations = [NSArray arrayWithObjects:pathAnim, nil];
anims.removedOnCompletion = NO;
anims.duration = 0.2f;
anims.beginTime = CACurrentMediaTime() + anims.duration*i;
anims.fillMode = kCAFillModeForwards;
[anims setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
[progressLayer addAnimation:anims forKey:nil];
There is no straightforward way to do this. I imagine the following would be easiest to implement:
Create a path per character that will trace how the stroke will be written. If you have limited number of characters like in the alphabet, it's possible to do this by hand. You can implement something like this to capture the path: http://code.tutsplus.com/tutorials/smooth-freehand-drawing-on-ios--mobile-13164
Use your current path (in red in your photo) as a clipping mask so the fill does not overflow.
Stroke the path you created in 1. with a very thick stroke to do the fill.
I can't find a "built-in" way to animate the fill as you want. There is a built-in way to animate the stroke, however:
Use a UIView with a CAShapeLayer backing it. CAShapeLayer has a path property. You can then apply an animation to the layer's strokeStart and/or strokeEnd properties.
Hope it helps.
After loading your bezierpath add init the animation:
CAKeyframeAnimation *shapeKeyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:#"strokeEnd"];
shapeKeyframeAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
shapeKeyframeAnimation.keyTimes = #[#0.0f, #0.6f, #1.0f];
shapeKeyframeAnimation.values = #[#0.0f, #1.0f, #1.0f];
shapeKeyframeAnimation.duration = 4.0f;
shapeKeyframeAnimation.repeatCount = CGFLOAT_MAX;
then add it to your shape layer:
[shapeLayer addAnimation:_loadingKeyframeAnimation forKey:#"stoke"];

Custom animation for CAEmitterCell

I have a CAEmitterLayer and I'd like to have a simple animation run over the course of each particle's life.
As soon as particle pops in, I'd like it to scale up to about 1.2, then after a short time have it scale back to 1.0 and stay that way until it's lifetime expires.
I know about the scale, scaleRange and scaleSpeed properties of the CAEmitterCell but they're way too random for what I need.
Is this possible to do? I've tried adding a CABasicAnimation like this (my CAEmitterCell's name is "heart"):
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"emitterCells.heart.scale"];
anim.fromValue = #(1.0);
anim.toValue = #(2.0);
anim.duration = 3.0;
anim.fillMode = kCAFillModeForwards;
anim.repeatCount = CGFLOAT_MAX;
[self.heartsEmitter addAnimation:anim forKey:#"scaleAnimation"];
but it doesn't work, the particles just appear at a random scale, they don't animate at all.
I'm not completely sure, but it seems to me that you are applying the animation to the emitter instead of the cells.
If the CAEmitterCell's name is heart try this: [self.heart addAnimation:anim forKey:#"scaleAnimation"];. Does this help?

How to synchronize animations of 2 different layers

I have a UIView whose layer has 2 sublayers, one is a CAShapeLayer, and the other a CALayer.
The CAShapeLayer has a path set using bezierPathWithOvalInRect, and is animated using CABasicAnimation. The "strokeEnd" property is animated from 0.0 to 1.0, over a certain duration. This has the effect of seeing the oval draw from beginning to end over the duration.
The CALayer simply has its contents set to a pencil image, and is animated using CAKeyframeAnimation. The "position" property is animated by setting the path property of the CAKeyframeAnimation to the same path as the CAShapeLayer, and the same duration as the CABasicAnimation. This has the effect of the pencil moving along the same path during the same duration, and it looks like the pencil is drawing the oval.
Works beautifully in iOS6. However, in iOS7, the timing is off - the position and strokeEnd animations are not in sync - they are in sync at specific moments, specifically at times 0, duration/4, duration/2, duration*3/4, and duration - but in between, the synchronization is off.
If instead of an ellipse, I use a rectangle or triangle, for example, it works great in iOS6 and iOS7. Only issue is an ellipse in iOS7.
Essentially I need to know how to synchronize 2 different animations, where each animation is animating a different layer.
Here is the code that creates the 2 layers:
self.drawingLayer = [CAShapeLayer layer];
self.drawingLayer.frame = self.view.bounds;
self.drawingLayer.bounds = drawingRect;
self.drawingLayer.path = path.CGPath;
self.drawingLayer.strokeColor = [[UIColor blackColor] CGColor];
self.drawingLayer.fillColor = nil;
self.drawingLayer.lineWidth = 10.0f;
self.drawingLayer.lineJoin = kCALineJoinRound;
self.drawingLayer.lineCap = kCALineJoinRound;
[self.view.layer addSublayer:self.drawingLayer];
UIImage *pencilImage = [UIImage imageNamed:#"pencil.png"];
self.pencilLayer = [CALayer layer];
self.pencilLayer.contents = (id)pencilImage.CGImage;
self.pencilLayer.contentsScale = [UIScreen mainScreen].scale;
self.pencilLayer.anchorPoint = CGPointMake(0.0, 1.0); //bottom left corner
self.pencilLayer.frame = CGRectMake(0.0f, 0.0f, 100.0f, 100.0f);
[self.view.layer addSublayer:self.pencilLayer];
And here is the code that creates and starts the 2 different animations (self.drawingLayer is the CAShapeLayer, and self.pencilLayer is the CALayer)
CABasicAnimation *drawingAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
drawingAnimation.duration = 10.0;
drawingAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
drawingAnimation.toValue = [NSNumber numberWithFloat:1.0f];
[self.drawingLayer addAnimation:drawingAnimation forKey:#"strokeEnd"];
CAKeyframeAnimation *pencilAnimation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
pencilAnimation.duration = drawingAnimation.duration;
pencilAnimation.path = self.drawingLayer.path;
pencilAnimation.calculationMode = kCAAnimationPaced;
pencilAnimation.delegate = self;
pencilAnimation.fillMode = kCAFillModeForwards;
pencilAnimation.removedOnCompletion = NO;
[self.pencilLayer addAnimation:pencilAnimation forKey:#"position"];
UPDATE
Here's a test app that illustrates the problem.
https://github.com/xjones/AnimatedPaths
Apple has informed me this is a known bug in iOS7, and they asked me to file a bug as well, to help them understand the demand for a fix. The tech support person at Apple told me he does not know of a workaround. If any of you figure out a workaround, I’m all ears...
I filed Apple bug reporter bug #15191834 on Oct 9, 2013. In my testing with iOS 8.1 and Xcode 6.1.1 the bug is still there in the Simulator and in the device. The bug was introduced in iOS 7 and has never been fixed.
Please file a bug so Apple can see others are impacted!

CALayer animations and color blending

I am using CAShapeLayer to generate some shapes. Actually I have 4 CAShapeLayers that represent some triangles in the screen. I create the shapes using CGMutablePathRef and setting the path property of each CAShapeLayer. Triangles are in different shapelayers since I have to animate each triangle in a different way.
I perform some animations on my viewcontroller using CABasicAnimation and animating the path property of the CAShapeLayer, something like:
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#”path”];
animation.duration = 2.0;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.repeatCount = 1e100f;
animation.autoreverses = YES;
animation.fromValue = (id)initialPath;
animation.toValue = (id)newPath;
[shapeLayer addAnimation:animation forKey:#”animatePath”];
That's ok, I can draw shapes and animate them. My problem is that this blends the images using the default alpha blending (result = (alpha * foreground) + (1 - alpha) * background). What I want is to use multiply blending (kCGBlendModeMultiply), but I don't know how to mix CAShapeLayer drawing and blending modes. I've successfully drawn my shapes creating a custom UIView and overriding the drawRect method, but then I can't animate the shapes.
Has anyone had any success changing the color blending of a CAShapeLayer?

how can I animate a CAlayer's bounds to progressively reveal an image?

For certain reasons, I'm trying to avoid using a CAScrollLayer to do this. The effect I'm going after is to progressively reveal (from bottom to top) a CALayer's content (a png I previously loaded in). So I thought about doing this:
layer.anchorPoint = CGPointMake(.5, 1);
CABasicAnimation* a = [CABasicAnimation animationWithKeyPath:#"bounds.size.height"];
a.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
a.fillMode = kCAFillModeBoth;
a.removedOnCompletion = NO;
a.duration = 1;
a.fromValue = [NSNumber numberWithFloat:0.];
a.toValue = [NSNumber numberWithFloat:layer.bounds.size.height];
[layer addAnimation:a forKey:nil];
The problem with this is you can tell the layer's content is scaled with the bounds. I was trying for the bounds to change but the content to stay always the original size, so that effectively the bounds clip the image and as I increase bounds.height, the image "Reveals" itself.
Any ideas as to how to pull it off or what might I be missing?
Ok I got it to work, but I basically had to update the layer's frame too, to reflect the change in anchor point:
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
layer.contentsGravity = kCAGravityTop;
layer.masksToBounds = YES;
layer.anchorPoint = CGPointMake(.5, 1);
CGRect newFrame = layer.frame;
newFrame.origin.y += newFrame.size.height / 2;
layer.frame = newFrame;
[CATransaction setValue:(id)kCFBooleanFalse forKey:kCATransactionDisableActions];
a.toValue = [NSNumber numberWithFloat:layer.bounds.size.height];
[layer addAnimation:a forKey:nil];
"Dad" has the right answer.
You want to create a CAShapeLayer, and install that as the mask on your layer.
You create a CGPath that is just a rectangle and install that path into the shape layer. The contents of the path determine what areas of the masked layer show up. If the path is a triangle in the middle of the layer, then only the triangle appears.
You then create an animation that animates the path.
To reveal your image from the bottom, you'd set up a path that was a 0 height rectangle at the bottom of the layer, and then you'd create a CAAnimation where the toValue is the same rectangle with a hight of the full layer you want to reveal. The system would generate an animation that reveals the image in a sweep.
You can use this same technique to achieve all kinds of cool effects, like barn doors, venetian blinds, "iris wipes", etc.
What if you changed the clipping mask instead? (or use a mask layer).
You could put another image over the target image and move it up like a stage curtain.

Resources