Adding scale animation for CAShapeLayer - ios

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"];

Related

UIView circle mask animation

I have an abstract strange shaped UIView.
And I need to display a smooth appearance animation.
I assume I should apply that animation to the mask above that view.
The mask is going to be circle shaped.
So user will see the 1'2'3'4' sequence.
How can I implement that?
I suppose I should apply some affine transformation
to the mask view. In that case, what should be the original shape of the mask?
A vertical line?
A simple example,suppose I have a 100*100 imageview,
Note:to make it simple,I use hard code numbers
CAShapeLayer * maskLayer = [CAShapeLayer layer];
maskLayer.bounds = CGRectMake(0, 0, 100, 100);
maskLayer.position = CGPointMake(50, 50);
UIBezierPath * path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(50.0, 50.0) radius:50 startAngle:0 endAngle:M_PI *2 clockwise:true];
maskLayer.path = path.CGPath;
maskLayer.fillColor = [UIColor clearColor].CGColor;
maskLayer.strokeColor = [UIColor blackColor].CGColor;
maskLayer.strokeEnd = 0.0;
maskLayer.lineWidth = 100;
self.imageview.layer.mask = maskLayer;
CABasicAnimation * animation = [CABasicAnimation animation];
animation.keyPath = #"strokeEnd";
animation.toValue = #(1.0);
animation.duration = 2.0;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
[maskLayer addAnimation:animation forKey:#"keyFrame"];

Control speed in CABasicAnimation

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.

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.

Display an animate two shapes

I have a CircleView : UIView class that animates (shrinks and grows) when you touch it (via the pop function). I would also like for a stroked circle to appear and grow out (like a basic water ripple) when you touch it too. (The effect I am talking about is shown here in CSS)
How do I go about doing this?
Some more information:
Ideally both animations would be controllable from the single CircleView class
CircleView inherits off UIView
Update
Based on answers I have added a new object to via a subLayer. This displays ok BUT it doesn't animate during the pop. Can anyone help me understand why?
Here is my current code (the pertinent bits anyway)
- (void)layoutSubviews
{
[self setLayerProperties];
}
- (void)setLayerProperties {
//The view’s Core Animation layer used for rendering.
CAShapeLayer *layer = (CAShapeLayer *)self.layer;
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)
byRoundingCorners:UIRectCornerAllCorners
cornerRadii:self.frame.size];
layer.path = bezierPath.CGPath;
layer.fillColor = _Color.CGColor;
rippleLayer = [[CALayer alloc] init];
layer.path = bezierPath.CGPath;
layer.strokeColor = [UIColor blueColor].CGColor;
[layer addSublayer:rippleLayer];
}
// Does the animated "pop"
- (void)pop{
CABasicAnimation *animation = [self animationWithKeyPath:#"transform.scale"];
[animation setFromValue:[NSNumber numberWithFloat:1.0f]];
[animation setToValue:[NSNumber numberWithFloat:0.8f]];
[animation setRemovedOnCompletion:YES];
[animation setDuration:0.15];
[animation setAutoreverses:YES];
[self.layer addAnimation:animation forKey:animation.keyPath];
rippleLayer.anchorPoint = CGPointMake(1, 1);
CABasicAnimation *scale = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
[scale setFromValue:[NSNumber numberWithFloat:1.0f]];
[scale setToValue:[NSNumber numberWithFloat:2.0f]];
[scale setRepeatCount:1];
[scale setDuration:1.0f];
//r[scale setRemovedOnCompletion:YES];
[scale setFillMode:kCAFillModeForwards];
[rippleLayer addAnimation:scale forKey:scale.keyPath];
}
Just add the other objects you want to animate to your layer and set their alpha to 0.0. You can then play with that value in or immediately before your animation.
I noticed that you did not follow the convention of naming variables with small initial. _color would be preferable to _Color.
Also, if you want to put the layer into its original state you usually use the identify transform matrix:
self.transform = CGAffineTransformIdentity;

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