iOS Obj-c gradient background transition - ios

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

Related

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

UIView Animating a 2D Bouncing Ball (Squash & Stretch) in 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"];

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

Animate views(layers) using CAKeyframeAnimation in a loop and CAAnimationGroup

I am trying to animate several views (layers) using CAKeyframeAnimation and CAAnimationGroup. Each view to be animated is contained in an array. My idea is to loop through the array where CAKeyframeAnimation instance of each view would be added to an array of animations, which then would be added to the CAAnimationGroup. My goal is to animate each view one after another. As far as I understand I need to use the group in order to have a reference for the beginTime.
I am probably missing something obvious, but my code doesn't work
CAAnimationGroup *cardAnimationGroup = [CAAnimationGroup animation];
cardAnimationGroup.delegate = self;
cardAnimationGroup.removedOnCompletion = NO;
cardAnimationGroup.beginTime = 0.0f;
[cardAnimationGroup setValue:#"animation0" forKey:#"id"];
cardAnimationGroup.duration = 1.2f * [_myViewsArray count];
NSMutableArray *animationsArray = [[NSMutableArray alloc] init];
// Loop through views
for (UIView *cardView in _myViewsArray)
{
NSUInteger index = [_myViewsArray indexOfObject:cardView];
// my target origin
CGFloat yOffset = (-(cardView.frame.origin.y - _middleCardView.frame.origin.y) - index * 2);
CAKeyframeAnimation *translateCardPositionAnimation = [CAKeyframeAnimation animationWithKeyPath:#"transform.translation.y"];
translateCardPositionAnimation.removedOnCompletion = YES;
//translateCardPositionAnimation.fillMode = kCAFillModeForwards;
translateCardPositionAnimation.beginTime = 1.2f * index;
translateCardPositionAnimation.duration = 1.2f;
translateCardPositionAnimation.values = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.0f],
[NSNumber numberWithFloat:yOffset],
[NSNumber numberWithFloat:yOffset],nil];
translateCardPositionAnimation.keyTimes = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.0f],
[NSNumber numberWithFloat:1.0f],
[NSNumber numberWithFloat:1.2f], nil];
[_containerCardView addSubview:cardView];
[_containerCardView sendSubviewToBack:cardView];
[animationsArray addObject:translateCardPositionAnimation];
[cardView.layer addObject:translateCardPositionAnimation forKey:#"id"];
if (index == [_myViewsArray count]-1) {
[cardAnimationGroup setAnimations:animationsArray];
}
}

Setting up AVSynchonizedLayer

I've been trying to produce a proof of concept that connects a video with an animation object (essentially, I am "rotoscoping" a user-selected-bitmap onto a canned video at specific locations). After watching all the available Apple material regarding Core Animation (WWDC10, etc.) and the AVSync..Layer (Apple WWDC10 sample code, doc sample code), there simply isn't a clear enough isolated code example to derive how to implemented this playback mode correctly.
Using CA, I have a video asset which is loaded into a CALayer, and an animation which is attached to a bitmap, and then connected to another CALayer. I have created an AVSynchronizedLayer and added both objects as sublayers.
When this Tree is added to the UIView's layer property I was expecting the playback of the animation to run in sync with the video. Instead, I only see the video playing, and the animation never executes - here are the relevant pieces of my code which all resides within a single View Controller (called within ViewDidLoad).
Video101.m4v = simple m4v video,
gerald.png = bitmap
NSURL *fileURL = [[NSBundle mainBundle] URLForResource:#"Video101" withExtension:#"m4v"];
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:fileURL options:nil];
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset];
AVPlayer *player = [[AVPlayer playerWithPlayerItem:playerItem]retain];
AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:player];
playerLayer.frame = CGRectMake(0, 0, 1024, 768);
playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
CALayer *gerald = [CALayer layer];
gerald.frame = CGRectMake(0, 0, 120, 120);
gerald.contents = (id) [UIImage imageNamed:#"gerald.png"].CGImage;
[self.view.layer addSublayer:playerLayer];
[self.view.layer insertSublayer:gerald above:playerLayer];
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:#"position"];
anim.duration = 18.0;
anim.values = [NSArray arrayWithObjects:
[NSValue valueWithCGPoint:CGPointMake(320.0, 406.0)],
[NSValue valueWithCGPoint:CGPointMake(310.0, 400.0)],
[NSValue valueWithCGPoint:CGPointMake(320.0, 406.0)],
[NSValue valueWithCGPoint:CGPointMake(310.0, 400.0)],
[NSValue valueWithCGPoint:CGPointMake(320.0, 406.0)],
[NSValue valueWithCGPoint:CGPointMake(310.0, 400.0)],
[NSValue valueWithCGPoint:CGPointMake(480.0, 206.0)],
[NSValue valueWithCGPoint:CGPointMake(310.0, 400.0)],
[NSValue valueWithCGPoint:CGPointMake(320.0, 406.0)],
[NSValue valueWithCGPoint:CGPointMake(320.0, 406.0)],
[NSValue valueWithCGPoint:CGPointMake(310.0, 400.0)],
[NSValue valueWithCGPoint:CGPointMake(320.0, 406.0)],
[NSValue valueWithCGPoint:CGPointMake(10.0, 760.0)],
[NSValue valueWithCGPoint:CGPointMake(320.0, 406.0)],
[NSValue valueWithCGPoint:CGPointMake(310.0, 400.0)],
[NSValue valueWithCGPoint:CGPointMake(320.0, 406.0)],
[NSValue valueWithCGPoint:CGPointMake(310.0, 400.0)], nil];
anim.keyTimes = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.02],
[NSNumber numberWithFloat:0.06],
[NSNumber numberWithFloat:0.09],
[NSNumber numberWithFloat:0.1],
[NSNumber numberWithFloat:0.12],
[NSNumber numberWithFloat:0.2],
[NSNumber numberWithFloat:0.25],
[NSNumber numberWithFloat:0.32],
[NSNumber numberWithFloat:0.38],
[NSNumber numberWithFloat:0.49],
[NSNumber numberWithFloat:0.53],
[NSNumber numberWithFloat:0.59],
[NSNumber numberWithFloat:0.63],
[NSNumber numberWithFloat:0.69],
[NSNumber numberWithFloat:0.78],
[NSNumber numberWithFloat:0.81],
[NSNumber numberWithFloat:0.91],
[NSNumber numberWithFloat:1.0], nil];
AVSynchronizedLayer *syncLayer = [AVSynchronizedLayer synchronizedLayerWithPlayerItem:playerItem];
[syncLayer addSublayer:gerald];
[gerald addAnimation:anim forKey:#"transform.position"];
[self.view.layer addSublayer:syncLayer];
[player play];
What is the correct format or manner to implement AVSynchronizedLayer, so that both animation and video playback together?
You have the CAKeyframeAnimation set up correctly except for one bit:
According to the WWDC "Editing Media with AV Foundation" lecture, you need to set the beginTime property of your animation to a non-zero value.
Zero beginTime is automatically translated to CACurrentMediaTime(). Use a small nonzero number: e.g., 1e-100 or -1e-100 (AVCoreAnimationBeginTimeAtZero)
I think if you add anim.beginTime = AVCoreAnimationBeginTimeAtZero; you'll find the animation begins right when the video starts playing.

Resources