UIView Animating a 2D Bouncing Ball (Squash & Stretch) in iOS - ios

Looking for Animating a view like Bouncing Ball using Squash & Stretch, in cocoa using core animation...
1 - Bouncing Ball Animation
;;;;
CABasicAnimation *animMoveUp = [CABasicAnimation animationWithKeyPath:#"position"];
animMoveUp.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
animMoveUp.fromValue = [NSValue valueWithCGPoint:_bottom];
animMoveUp.toValue = [NSValue valueWithCGPoint:_top];
CABasicAnimation *scaleAnim1 = [CABasicAnimation animationWithKeyPath:#"transform"];
scaleAnim1.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(2.5, 2.0, 1.0)];
scaleAnim1.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(2.0, 2.0, 1.0)];
CABasicAnimation *bounceAnim1 = [CABasicAnimation animationWithKeyPath:#"transform"];
bounceAnim1.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(2.0, 2.0, 1.0)];
bounceAnim1.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(3.0, 1.0, 1.0)];
CABasicAnimation *animMoveDown = [CABasicAnimation animationWithKeyPath:#"position"];
animMoveDown.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animMoveDown.fromValue = [NSValue valueWithCGPoint:_top];
animMoveDown.toValue = [NSValue valueWithCGPoint:_bottom];
CABasicAnimation *scaleAnim2 = [CABasicAnimation animationWithKeyPath:#"transform"];
scaleAnim2.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(2.5, 2.0, 1.0)];
scaleAnim2.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(2.0, 2.0, 1.0)];
CABasicAnimation *bounceAnim2 = [CABasicAnimation animationWithKeyPath:#"transform"];
bounceAnim2.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(2.0, 2.0, 1.0)];
bounceAnim2.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(3.0, 1.0, 1.0)];
CABasicAnimation *animMoveUp1 = [CABasicAnimation animationWithKeyPath:#"position"];
animMoveUp1.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animMoveUp1.fromValue = [NSValue valueWithCGPoint:_bottom];
animMoveUp1.toValue = [NSValue valueWithCGPoint:_top];
CABasicAnimation *scaleAnim3 = [CABasicAnimation animationWithKeyPath:#"transform"];
scaleAnim3.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(2.5, 2.0, 1.0)];
scaleAnim3.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(2.0, 2.0, 1.0)];
CABasicAnimation *bounceAnim3 = [CABasicAnimation animationWithKeyPath:#"transform"];
bounceAnim3.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(2.0, 2.0, 1.0)];
bounceAnim3.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(3.0, 1.0, 1.0)];
CABasicAnimation *animMoveDown2 = [CABasicAnimation animationWithKeyPath:#"position"];
animMoveDown2.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animMoveDown2.fromValue = [NSValue valueWithCGPoint:_top];
animMoveDown2.toValue = [NSValue valueWithCGPoint:_bottom];
CABasicAnimation *scaleAnim4 = [CABasicAnimation animationWithKeyPath:#"transform"];
scaleAnim4.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(2.5, 2.0, 1.0)];
scaleAnim4.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(2.0, 2.0, 1.0)];
CABasicAnimation *bounceAnim4 = [CABasicAnimation animationWithKeyPath:#"transform"];
bounceAnim4.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(2.0, 2.0, 1.0)];
bounceAnim4.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(3.0, 1.0, 1.0)];
CAAnimationGroup *animGroup = [CAAnimationGroup animation];
animGroup.animations = #[animMoveUp, scaleAnim1, bounceAnim1,
animMoveDown, scaleAnim2, bounceAnim2,
animMoveUp1, scaleAnim3, bounceAnim3,
animMoveDown2, scaleAnim4, bounceAnim4];
animGroup.duration = 2.5;
animGroup.delegate = self;
animGroup.fillMode = kCAFillModeForwards;
animGroup.removedOnCompletion = NO;
[_imgView.layer addAnimation:animGroup forKey:nil];

The following code will generate the animation above:
CAKeyframeAnimation *trans = [CAKeyframeAnimation animationWithKeyPath:#"transform.translation.y"];
NSArray *values = #[#(-200),#(20),#(0)];
trans.values = values;
NSArray *times = #[#(0.0),#(0.85),#(1)];
trans.keyTimes = times;
trans.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
trans.autoreverses = YES;
trans.duration = 1.0;
trans.repeatCount = INFINITY;
CAKeyframeAnimation *scaleXAnimation = [CAKeyframeAnimation animationWithKeyPath:#"transform.scale.x"];
NSArray *scaleXValues = #[#(0.75),#(0.75),#(1)];
scaleXAnimation.values = scaleXValues;
NSArray *scaleXtimes = #[#(0.0),#(0.85),#(1)];
scaleXAnimation.keyTimes = scaleXtimes;
scaleXAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
scaleXAnimation.autoreverses = YES;
scaleXAnimation.duration = 1.0;
scaleXAnimation.repeatCount = INFINITY;
CAKeyframeAnimation *scaleYAnimation = [CAKeyframeAnimation animationWithKeyPath:#"transform.scale.y"];
NSArray *scaleYValues = #[#(0.75),#(1),#(0.85)];
scaleYAnimation.values = scaleYValues;
NSArray *scaleYtimes = #[#(0.1),#(0.5),#(1)];
scaleYAnimation.keyTimes = scaleYtimes;
scaleYAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
scaleYAnimation.autoreverses = YES;
scaleYAnimation.duration = 1.0;
scaleYAnimation.repeatCount = INFINITY;
[_ballView.layer addAnimation:scaleXAnimation forKey:#"scaleX"];
[_ballView.layer addAnimation:scaleYAnimation forKey:#"scaleY"];
[_ballView.layer addAnimation:trans forKey:#"trans"];

Related

iOS Obj-c gradient background transition

I'd like to gradually change the background gradient from
gradient.colors = [NSArray arrayWithObjects:(id)[UIColor.blackColor CGColor],(id)[UIColor.blueColor CGColor], nil];
to
gradient.colors = [NSArray arrayWithObjects:(id)[UIColor.grayColor CGColor],(id)[UIColor.cyanColor CGColor], nil];
[EDIT] My current code is:
gradient.colors = [NSArray arrayWithObjects:(id)[UIColor.blackColor CGColor],(id)[UIColor.blueColor CGColor], nil];
NSArray *fromColors = gradient.colors;
NSArray *toColors =[NSArray arrayWithObjects:(id)[UIColor.grayColor CGColor],(id)[UIColor.cyanColor CGColor], nil];
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath: #"colors"];
animation.fromValue = fromColors;
animation.toValue = toColors;
animation.duration = 3.00;
animation.removedOnCompletion = false;
animation.autoreverses = NO;
animation.fillMode = kCAFillModeForwards;
animation.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionLinear];
if (![gradient.animationKeys containsObject:#"colors"]) {
[gradient addAnimation:animation forKey:#"colors"];
}
[self.background.layer addSublayer:gradient];
But it doesn't do anything. Can someone help me please?
I don't think "colors" are supported in CABasicAnimation.keypath.But how to meet the requirement, I still can't think of the answer.https://stackoverflow.com/questions/5459673/how-can-i-know-the-values-in-cabasicanimation-keypath

Draw letter(text) with UIBezierPath single line

I am using this and this for reference. I am new for using UIBezierPath, and i want to draw path on character(letter) code i am using is as follow.
CGMutablePathRef letters = CGPathCreateMutable();
CTFontRef font = CTFontCreateWithName(CFSTR("Courier New"), 200.0f, NULL);
NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys:CFBridgingRelease(font), kCTFontAttributeName,nil];
NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:#"B"
attributes:attrs];
CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)attrString);
CFArrayRef runArray = CTLineGetGlyphRuns(line);
// for each RUN
for (CFIndex runIndex = 0; runIndex < CFArrayGetCount(runArray); runIndex++)
{
// Get FONT for this run
CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runArray, runIndex);
CTFontRef runFont = CFDictionaryGetValue(CTRunGetAttributes(run), kCTFontAttributeName);
// for each GLYPH in run
for (CFIndex runGlyphIndex = 0; runGlyphIndex < CTRunGetGlyphCount(run); runGlyphIndex++)
{
// get Glyph & Glyph-data
CFRange thisGlyphRange = CFRangeMake(runGlyphIndex, 1);
CGGlyph glyph;
CGPoint position;
CTRunGetGlyphs(run, thisGlyphRange, &glyph);
CTRunGetPositions(run, thisGlyphRange, &position);
// Get PATH of outline
{
CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyph, NULL);
CGAffineTransform t = CGAffineTransformMakeTranslation(position.x, position.y);
CGPathAddPath(letters, &t, letter);
CGPathRelease(letter);
}
}
}
CFRelease(line);
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointZero];
[path appendPath:[UIBezierPath bezierPathWithCGPath:letters]];
CGPathRelease(letters);
CFRelease(font);
NSLog(#"==> %#",path);
// Creates layer
CAShapeLayer *pathLayer = [CAShapeLayer layer];
pathLayer.frame = drawingView.bounds;
pathLayer.bounds = CGPathGetBoundingBox(path.CGPath);
//pathLayer.backgroundColor = [[UIColor yellowColor] CGColor];
pathLayer.geometryFlipped = NO;
pathLayer.path = path.CGPath;
pathLayer.strokeColor = [[UIColor blackColor] CGColor];
pathLayer.fillColor = nil;
pathLayer.lineWidth = 3.0f;
pathLayer.lineJoin = kCALineJoinBevel;
[drawingView.layer addSublayer:pathLayer];
self.pathLayer = pathLayer;
// Add pointer
UIImage *penImage = [UIImage imageNamed:#"hand.png"];
CALayer *penLayer = [CALayer layer];
penLayer.contents = (id)penImage.CGImage;
penLayer.anchorPoint = CGPointMake(0.35f, 0.98f);
penLayer.frame = CGRectMake(0.0f, 0.0f, penImage.size.width, penImage.size.height);
[pathLayer addSublayer:penLayer];
self.penLayer = penLayer;
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
pathAnimation.duration = 5.0;
pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
[self.pathLayer addAnimation:pathAnimation forKey:#"strokeEnd"];
CAKeyframeAnimation *penAnimation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
penAnimation.duration = 5.0;
penAnimation.path = self.pathLayer.path;
penAnimation.calculationMode = kCAAnimationPaced;
penAnimation.delegate = self;
[self.penLayer addAnimation:penAnimation forKey:#"position"];
This code draws latter latter in double line like the image below
instead i want to draw latter in single line like the image below
and the hand should draw the lines on centre of the font and not the border.

How do I reset a CABasicAnimation?

I have an animation which is a bar, which travels across the screen according to a timer which shortens each time something is tapped. My problem is that the animation only plays once. I try calling the animation to reset and begin the bar again, but nothing happens. Here is some code:
-(IBAction)targetTapped:(id)sender {
[Time invalidate];
targX = arc4random() %619;
targY = arc4random() %925;
NSLog([NSString stringWithFormat:#"X = %li", (long)targX]);
NSLog([NSString stringWithFormat:#"Y = %li", (long)targY]);
Target.center = CGPointMake(targX, targY);
Score = Score + 1;
if (Score >= 225) {
timeMax = 0.5;
}
else {
timeMax = 5 - (Score * 0.02);
}
Time = [NSTimer scheduledTimerWithTimeInterval:timeMax target:self selector:#selector(Lose) userInfo:nil repeats:NO];
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(0.0,895.0)];
[path addLineToPoint:CGPointMake(768.0, 895.0)];
CAShapeLayer *pathLayer = [CAShapeLayer layer];
pathLayer.frame = self.view.bounds;
pathLayer.path = path.CGPath;
pathLayer.strokeColor = [[UIColor blueColor] CGColor];
pathLayer.fillColor = nil;
pathLayer.lineWidth = 5.0f;
pathLayer.lineJoin = kCALineJoinBevel;
[self.view.layer addSublayer:pathLayer];
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
pathAnimation.duration = timeMax;
pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
[pathLayer animationForKey:#"strokeEnd"];
[pathLayer addAnimation:pathAnimation forKey:#"strokeEnd"];
}
You can create the pathLayer once:
[self.pathLayer removeAnimationForKey:#"strokeEnd"];
if (self.pathLayer == nil) {
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(0.0,895.0)];
[path addLineToPoint:CGPointMake(768.0, 895.0)];
CAShapeLayer *pathLayer = [CAShapeLayer layer];
pathLayer.frame = self.view.bounds;
pathLayer.path = path.CGPath;
pathLayer.strokeColor = [[UIColor blueColor] CGColor];
pathLayer.fillColor = nil;
pathLayer.lineWidth = 5.0f;
pathLayer.lineJoin = kCALineJoinBevel;
[self.view.layer addSublayer:pathLayer];
self.pathLayer = pathLayer;
}
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
pathAnimation.duration = timeMax;
pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
[self.pathLayer addAnimation:pathAnimation forKey:#"strokeEnd"];

iOS simultaneous translation and scaling issue (CAAnimation)

I have a method which translates a view and optionally scales it while translating. I am using animation groups and adding the required animations as needed. Here are the animation cases working perfectly.
Translation (x or y axis or both) (OK)
Scaling (OK)
Translation while scaling (MOSTLY)
However, doing a translation while scaling works only when the starting scalefactor is 1. When the view is already scaled, I notice most of the transition jumping instead of animating, although the end result of scaling and translation is correct.
I am posting part of the code for clarification. The problem occurs only when previousZoomScale is not 1! I appreciate any directions you can point me to.
-(void)performAnimationForView: (AnimationType)animationType
WithDX: (CGFloat)dx
AndDY: (CGFloat)dy
Scale: (CGFloat)scale
Duration: (CGFloat)duration
{
CABasicAnimation *translateXAnimation = nil, *translateYAnimation = nil, *scaleAnimation = nil;
NSString* animationGroupName;
CGPoint newDxDy;
switch (animationType)
{
case kAnimationTranslateAndScale:
// set animation key
animationGroupName = #"translatescale";
translateXAnimation = [CABasicAnimation animationWithKeyPath:#"transform.translation.x"];
translateXAnimation.repeatCount = 0;
translateXAnimation.autoreverses = NO;
translateXAnimation.removedOnCompletion = NO;
translateXAnimation.fillMode = kCAFillModeForwards;
translateXAnimation.fromValue = [NSNumber numberWithFloat: self.previousDxDy.x]; // previous point we're tranlsating from
translateXAnimation.toValue = [NSNumber numberWithFloat:dx]; // destination point
translateYAnimation = [CABasicAnimation animationWithKeyPath:#"transform.translation.y"];
translateYAnimation.repeatCount = 0;
translateYAnimation.autoreverses = NO;
translateYAnimation.removedOnCompletion = NO;
translateYAnimation.fillMode = kCAFillModeForwards;
translateYAnimation.fromValue = [NSNumber numberWithFloat: self.previousDxDy.y];
translateYAnimation.toValue = [NSNumber numberWithFloat:dy];
newDxDy = CGPointMake(dx, dy);
self.previousDxDy = newDxDy;
// scaling animation
scaleAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
scaleAnimation.autoreverses = NO;
scaleAnimation.repeatCount = 0;
scaleAnimation.removedOnCompletion = NO;
scaleAnimation.fillMode = kCAFillModeForwards;
scaleAnimation.fromValue = [NSNumber numberWithDouble: self.previousZoomScale]; // initial zoom scale
scaleAnimation.toValue = [NSNumber numberWithDouble:scale]; // target scale
self.previousZoomScale = scale;
break;
}
NSMutableArray* animationArray = [[NSMutableArray alloc] initWithObjects: nil];
if (translateXAnimation != nil)
[animationArray addObject: translateXAnimation];
if (translateYAnimation != nil)
[animationArray addObject: translateYAnimation];
if (scaleAnimation != nil)
[animationArray addObject: scaleAnimation];
CAAnimationGroup *theGroup = [CAAnimationGroup animation];
theGroup.duration = duration;
theGroup.removedOnCompletion = NO;
theGroup.fillMode = kCAFillModeForwards;
theGroup.repeatCount = 0;
theGroup.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionLinear];
theGroup.animations = animationArray;
theGroup.delegate = self;
[theGroup setValue: animationGroupName forKey: #"animationtype"];
[self.backgroundImageView.layer addAnimation: theGroup forKey: animationGroupName];
}

how to use CAKeyframeAnimation is for Bouncing effect IOS

I made this code to create CAKeyframeAnimation but result is not bouncing at all
-(CAKeyframeAnimation *)keyframeBounceAnimationFrom:(NSValue *)from
to:(NSValue *)to
forKeypath:(NSString *)keyPath
withDuration:(CFTimeInterval)duration
{
CAKeyframeAnimation * animation = [CAKeyframeAnimation animationWithKeyPath:keyPath];
NSMutableArray * valuesArray = [NSMutableArray array];
NSMutableArray * timeKeyArray = [NSMutableArray array];
[self createBounceFrom:from to:to Values:valuesArray keyTimes:timeKeyArray];
[animation setValues:valuesArray];
[animation setKeyTimes:timeKeyArray];
animation.duration = duration;
return animation;
}
-(void)createBounceFrom:(NSValue *)from to:(NSValue *)to Values:(NSMutableArray *)values keyTimes:(NSMutableArray *)keyTimes
{
CGPoint toPoint= [to CGPointValue];
CGFloat offset = 60;
CGFloat duration = 1.0f;
NSUInteger numberOfOscillations= 4;
[values addObject:from];
[keyTimes addObject:[NSNumber numberWithFloat:0.0f]];
//============
//ideally bouncing will depend from starting position to end poisiton as simulating real Dumping Oscillations
//============
for (NSUInteger index= 0; index <numberOfOscillations ; index++)
{
CGPoint viaPoint = CGPointMake(toPoint.x, toPoint.y + offset);
NSValue * via = [NSValue valueWithCGPoint:viaPoint];
[values addObject:via];
//add time consumed for each oscillation
[keyTimes addObject:[NSNumber numberWithFloat:duration]];
// duration = duration - 0.1;
offset = - offset ;
}
[values addObject:to];
[keyTimes addObject:[NSNumber numberWithFloat:0.6]];
}
Here's a great link with some examples of how to use it:
(After some more looking I found this: http://www.cocoanetics.com/2012/06/lets-bounce/ which I think makes a much better bounce, but more advanced. So wont put the code here)
http://www.touchwonders.com/some-techniques-for-bouncing-animations-in-ios/
The example with CAKeyframeAnimation would be: (taken from the link)
-(void)animateGreenBall
{
NSValue *from = #(greenBall.layer.position.y);
CGFloat bounceDistance = 20.0;
CGFloat direction = (greenUp? 1 : -1);
NSValue *via = #((greenUp ? HEIGHT_DOWN : HEIGHT_UP) + direction*bounceDistance);
NSValue *to = greenUp ? #(HEIGHT_DOWN) : #(HEIGHT_UP);
NSString *keyPath = #"position.y";
CAKeyframeAnimation *animation = [self keyframeBounceAnimationFrom:from
via:via
to:to
forKeyPath:keyPath
withDuration:.6];
[greenBall.layer addAnimation:animation forKey:#"bounce"];
[greenBall.layer setValue:to forKeyPath:keypath];
greenUp = !greenUp;
}
-(CAKeyframeAnimation *)keyframeBounceAnimationFrom:(NSValue *)from
via:(NSValue *)via
to:(NSValue *)to
forKeyPath:(NSString *)keyPath
withDuration:(CFTimeInterval)duration
{
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:keyPath];
[animation setValues:#[from, via, to, nil]];
[animation setKeyTimes:#[#(0), #(.7), #(1), nil]];
animation.duration = duration;
return animation;
}

Resources