I've been trying to implement this but couldn't find any code that could shake it like iPhone app shakes while deleting them!
CABasicAnimation *animation =
[CABasicAnimation animationWithKeyPath:#"position"];
[animation setDuration:0.05];
[animation setRepeatCount:8];
[animation setAutoreverses:YES];
[animation setFromValue:[NSValue valueWithCGPoint:
CGPointMake([lockView center].x - 20.0f, [lockView center].y)]];
[animation setToValue:[NSValue valueWithCGPoint:
CGPointMake([lockView center].x + 20.0f, [lockView center].y)]];
[[lockView layer] addAnimation:animation forKey:#"position"];
you can achieve this by the following code, you have define the rotation angle from and to.
CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
[anim setToValue:[NSNumber numberWithFloat:0.0f]];
[anim setFromValue:[NSNumber numberWithDouble:M_PI/16]]; // rotation angle
[anim setDuration:0.1];
[anim setRepeatCount:NSUIntegerMax];
[anim setAutoreverses:YES];
[[imageView layer] addAnimation:anim forKey:#"iconShake"];
try this:
- (void)animateImage
{
CAKeyframeAnimation* animation = [CAKeyframeAnimation animationWithKeyPath:#"transform"];
CGFloat wobbleAngle = 0.06f;
NSValue* valLeft = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(wobbleAngle, 0.0f, 0.0f, 1.0f)];
NSValue* valRight = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(-wobbleAngle, 0.0f, 0.0f, 1.0f)];
animation.values = [NSArray arrayWithObjects:valLeft, valRight, nil];
animation.autoreverses = YES;
animation.duration = 0.125;
animation.repeatCount = HUGE_VALF;
[[lockView layer] addAnimation:animation forKey:#"shakeAnimation"];
}
Related
I want to mask CALayer with another layer whose frame I want to keep changing in every draw call thereby animating. Is it possible to do change the frame of mask layer in every draw call rather than making a new mask layer?
Try the following code. This code will run on Cocoa App for macOS. Kindly change the classes accordingly if you are going for Cocoa Touch for iOS. I hope this works for you.
// ViewController.h
import
#interface ViewController : NSViewController{
CALayer * maskLayer;
CALayer * Layer;
}
// ViewController.m
#implementation ViewController
-(void)viewDidLoad{
[super viewDidLoad];
maskLayer = [CALayer layer];
Layer = [CALayer layer];
Layer.backgroundColor = [[NSColor blueColor] CGColor];
Layer.frame = CGRectMake(20, 20, 720, 900);
Layer.contents = [NSImage imageNamed:#"bg1"];
maskLayer.frame = CGRectMake(20, 20, 720, 900);
maskLayer.opacity = 0.5;
[maskLayer mask];
maskLayer.contents = [NSImage imageNamed:#"bg"];
[self.view.layer addSublayer:Layer];
[self.view.layer addSublayer:maskLayer];
[self maskLayerAnimation];
}
-(void)maskLayerAnimation{
[CATransaction begin];
CABasicAnimation* fadeAnim = [CABasicAnimation animationWithKeyPath:#"opacity"];
fadeAnim.fromValue = [NSNumber numberWithFloat:1.0];
fadeAnim.toValue = [NSNumber numberWithFloat:0.0];
fadeAnim.duration = 5.0;
[CATransaction setCompletionBlock:^{
NSLog(#"animation completed");
maskLayer.frame = CGRectMake(20, 20, 720, 700);
maskLayer.contents = [NSImage imageNamed:#"bg2"];
}];;
[maskLayer addAnimation:fadeAnim forKey:#"opacity"];
[CATransaction commit];
}
I'd like to change the Speed of my "Marching ants" effect after it has looped every 10 times. So it's looping the animation with a duration of 1, after the 10 loops it should change the duration to 0.95, after 10 more seconds to 0.9 and so on....
Do you have an idea how I could do this?
Here's the code for my current animation:
CAShapeLayer *lineLayer = [CAShapeLayer layer];
lineLayer.bounds = CGRectMake(0, 0, self.view.bounds.size.width, 20);
lineLayer.position = self.view.center;
lineLayer.strokeColor = [UIColor whiteColor].CGColor;
lineLayer.lineDashPattern = #[#100];
lineLayer.lineWidth = CGRectGetHeight(lineLayer.bounds);
UIBezierPath *linePath = [UIBezierPath bezierPath];
[linePath moveToPoint:CGPointMake(0, CGRectGetMidY(lineLayer.bounds))];
[linePath addLineToPoint:CGPointMake(CGRectGetMaxX(lineLayer.bounds), CGRectGetMidY(lineLayer.bounds))];
lineLayer.path = linePath.CGPath;
// [self.view.layer addSublayer:lineLayer];
NSNumber *totalDashLenght = [lineLayer.lineDashPattern valueForKeyPath:#"#sum.self"]; // KVC is awesome :)
CABasicAnimation *animatePhase = [CABasicAnimation animationWithKeyPath:#"lineDashPhase"];
animatePhase.byValue = totalDashLenght; // using byValue means that even if the layer has a shifted phase, it will shift on top of that.
animatePhase.duration = 0.5;
animatePhase.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
animatePhase.repeatCount = 10;
// [lineLayer addAnimation:animatePhase forKey:#"marching ants"];
// Create the shape layer
shapeLayer = [CAShapeLayer layer];
CGRect shapeRect = CGRectMake(0.0f, 0.0f, 2200.0f, 2000.0f);
[shapeLayer setBounds:shapeRect];
[shapeLayer setPosition:CGPointMake(160.0f, 1350.0f)];
[shapeLayer setFillColor:[[UIColor clearColor] CGColor]];
[shapeLayer setStrokeColor:[[UIColor whiteColor] CGColor]];
[shapeLayer setLineWidth:20.0f];
[shapeLayer setLineJoin:kCALineJoinRound];
[shapeLayer setLineDashPattern:
[NSArray arrayWithObjects:[NSNumber numberWithInt:200],
[NSNumber numberWithInt:100],
nil]];
// Setup the path
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, shapeRect);
[shapeLayer setPath:path];
CGPathRelease(path);
// Set the layer's contents
//[shapeLayer setContents:(id)[[UIImage imageNamed:#"GEIST.jpg"] CGImage]];
[[[self view] layer] addSublayer:shapeLayer];
if ([shapeLayer animationForKey:#"linePhase"])
[shapeLayer removeAnimationForKey:#"linePhase"];
else {
CABasicAnimation *dashAnimation;
dashAnimation = [CABasicAnimation
animationWithKeyPath:#"lineDashPhase"];
[dashAnimation setFromValue:[NSNumber numberWithFloat:0.0f]];
[dashAnimation setToValue:[NSNumber numberWithFloat:300.0f]];
[dashAnimation setDuration:1];
[dashAnimation setRepeatCount:10];
[shapeLayer addAnimation:dashAnimation forKey:#"linePhase"];
Just set yourself up to detect the end of your animation, either with a delegate method or with a completion block on the transaction. When your animation completes its ten repetitions, you can schedule a new animation with a shorter duration.
(And remember being small, playing under the table and dreaming...)
I'm drawing a UIBezierPath on a UIScrollView I have made an animation that draws the path from start to end point but this is not the animation that I want.
UIBezierPath *linePath = [UIBezierPath bezierPath];
[linePath moveToPoint:startPoints];
[linePath addLineToPoint:endPoints;
//shape layer for the line
CAShapeLayer *line = [CAShapeLayer layer];
line.path = [linePath CGPath];
// line.fillColor = [[UIColor blackColor] CGColor];
line.strokeColor = [[colors objectAtIndex:i] CGColor];
line.lineWidth = 5;
// line.contents = (id)[[UIImage imageNamed:#"Mask.png"] CGImage];
// line.contentsGravity = kCAGravityCenter;
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
pathAnimation.duration = 3.0;
pathAnimation.fromValue = #(0.0f);
pathAnimation.toValue = #(1.0f);
pathAnimation.repeatCount = HUGE_VAL;
[line addAnimation:pathAnimation forKey:#"strokeEnd"];
I have tried adding a contents to the shape layer but I'm bad at animations. The effect I want to achieve is the same animation as "slide to unlock" has, or a path that pulses.
I've tried to do the same thing as the answer from slide-to-unlock but can't seem to manage
I ended up dooing this:
UIBezierPath *linePath = [UIBezierPath bezierPath];
[linePath moveToPoint:startPoints];
[linePath addLineToPoint:endPoints];
//gradient layer for the line
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = CGRectMake(0, 0, 150.0, 1.0);
gradient.cornerRadius = 5.0f;
gradient.startPoint = CGPointMake(0.0, 0.5);
gradient.endPoint = CGPointMake(1.0, 0.5);
gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor clearColor] CGColor],(id)[[UIColor whiteColor] CGColor],(id)[[UIColor blueColor] CGColor],(id)[[UIColor clearColor] CGColor], nil];
[scrollViewContent.layer addSublayer:gradient];
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:#"position"];
anim.path = [linePath CGPath];
anim.rotationMode = kCAAnimationRotateAuto;
anim.repeatCount = 0;
anim.duration = 1;
[gradient addAnimation:anim forKey:#"gradient"];
I'm in need of an UIImage from a Masked CALayer. This is the function I use:
- (UIImage *)imageFromLayer:(CALayer *)layer
{
UIGraphicsBeginImageContext([layer frame].size);
[layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return outputImage;
}
The problem is that the mask isn't maintained.
This is the completed code:
CAShapeLayer * layerRight= [CAShapeLayer layer];
layerRight.path = elasticoRight;
im2.layer.mask = layerRight;
CAShapeLayer * layerLeft= [CAShapeLayer layer];
layerLeft.path = elasticoLeft;
im3.layer.mask = layerLeft;
[viewImage.layer addSublayer:im2.layer];
[viewImage.layer addSublayer:im3.layer];
UIImage *image_result = [self imageFromLayer:viewImage.layer];
If I visualize the viewImage, the result is correct, but if I try to obtain the image relative to the layer, the masks are lost.
I've solved.
Now i obtaining the image mask and use CGContextClipToMask.
CGRect rect = CGRectMake(0, 0, 1024, 768);
UIGraphicsBeginImageContextWithOptions(rect.size, YES, 0.0);
{
[[UIColor blackColor] setFill];
UIRectFill(rect);
[[UIColor whiteColor] setFill];
UIBezierPath *leftPath = [UIBezierPath bezierPath];
// Set the starting point of the shape.
CGPoint p1 = [(NSValue *)[leftPoints objectAtIndex:0] CGPointValue];
[leftPath moveToPoint:CGPointMake(p1.x, p1.y)];
for (uint i=1; i<leftPoints.count; i++)
{
CGPoint p = [(NSValue *)[leftPoints objectAtIndex:i] CGPointValue];
[leftPath addLineToPoint:CGPointMake(p.x, p.y)];
}
[leftPath closePath];
[leftPath fill];
}
UIImage *mask = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.0);
{
CGContextClipToMask(UIGraphicsGetCurrentContext(), rect, mask.CGImage);
[im_senza drawAtPoint:CGPointZero];
}
UIImage *maskedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
I am developing a maps application. I draw an animated line in the map and then use an image (a ruler image) which should be draw just over the line. To draw the line I use a CABasicAnimation with path:
-(void)lineaAnimation {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"path"];
animation.duration = animDuration;
animation.delegate=self;
animation.repeatCount = 0;
animation.autoreverses = NO;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.fromValue = (id)inip;
animation.toValue = (id)finp;
animation.fillMode=kCAFillModeForwards;
animation.removedOnCompletion=NO;
[shapeLayer addAnimation:animation forKey:#"animatePath"];
}
-(void)linea:(CGPoint)origen to:(CGPoint)destino animado:(BOOL)anim duracion:(float)durac inicio:(float)delay {
inip = CGPathCreateMutable();
finp = CGPathCreateMutable();
CGPathMoveToPoint(inip, nil, origen.x, origen.y);
CGPathAddLineToPoint(inip, nil, origen.x, origen.y);
CGPathCloseSubpath(inip);
CGPathMoveToPoint(finp, nil, origen.x, origen.y);
CGPathAddLineToPoint(finp, nil, destino.x, destino.y);
CGPathCloseSubpath(finp);
rootLayer = [CALayer layer];
rootLayer.frame = fondo.bounds;
[fondo.layer addSublayer:rootLayer];
shapeLayer = [CAShapeLayer layer];
shapeLayer.path = inip;
UIColor *fillColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.7];
shapeLayer.fillColor = fillColor.CGColor;
UIColor *strokeColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.7];
shapeLayer.strokeColor = strokeColor.CGColor;
shapeLayer.lineWidth = 5.0;
shapeLayer.fillRule = kCAFillRuleEvenOdd;
[morrallaLayer addObject:shapeLayer];
[rootLayer addSublayer:shapeLayer];
animDuration=durac;
[self performSelector:#selector(lineaAnimation) withObject:nil afterDelay:delay];
}
Then I try to move the center of the rule to the point and rotate the ruler image using this code:
delta=acos((destino.y-origen.y)/(destino.x-origen.x));
giroIni=0.0;
giroFin=delta;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
animation.fromValue= [NSNumber numberWithFloat:giroIni];
animation.toValue= [NSNumber numberWithFloat:giroFin];
animation.delegate = self;
animation.repeatCount = 0;
animation.autoreverses = NO;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.duration = animDuration;
animation.fillMode=kCAFillModeForwards;
animation.removedOnCompletion=NO;
[ruleL addAnimation:animation forKey:#"giroAngulo"];
The problem is that the ruler borderline doesn't match the line draw, there are around a degree of difference depending on the final orientation... why?
Thanks a lot!
Ooooops... sorry for your time :(
I found the problem and.. as usual the error is not in the stars but in ourselves
I use the acos function when OBVIOUSLY I should use atan