Flickering in simple animation chain - ios

I'm trying to animate a pulsting circle. But after the downward scale, there's something of a flickering effect. Any ideas to why that is? Here's my code so far:
- (instancetype)init
{
self = [super init];
if (self) {
if (!self.path) {
self.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(0, 0) radius:7 startAngle:0.0*(M_PI/180.0) endAngle:360.0*(M_PI/180.0) clockwise:YES].CGPath;
}
[self animateCircleUpward];
}
return self;
}
-(void)animateCircleUpward {
[CATransaction begin];
[CATransaction setCompletionBlock:^{[self animateCircleDownward];}];
CABasicAnimation * scaleUpwardAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale.xy"];
scaleUpwardAnimation.fromValue = #(0.7);
scaleUpwardAnimation.toValue = #(1.0);
CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:#"opacity"];
opacityAnimation.fromValue = #(1.0);
opacityAnimation.toValue = #(0.6);
self.fillColor = [UIColor greenColor].CGColor;
CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
NSArray *animations = #[scaleUpwardAnimation, opacityAnimation];
animationGroup.duration = 0.4;
animationGroup.animations = animations;
[self addAnimation:animationGroup forKey:#"pulse"];
[CATransaction commit];
}
-(void)animateCircleDownward {
[CATransaction begin];
[CATransaction setCompletionBlock:^{[self animateCircleUpward];}];
CABasicAnimation * scaleDownwardAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale.xy"];
scaleDownwardAnimation.fromValue = #(1.0);
scaleDownwardAnimation.toValue = #(0.7);
CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:#"opacity"];
opacityAnimation.fromValue = #(0.6);
opacityAnimation.toValue = #(1.0);
self.fillColor = [UIColor greenColor].CGColor;
CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
NSArray *animations = #[scaleDownwardAnimation, opacityAnimation];
animationGroup.duration = 0.4;
animationGroup.animations = animations;
[self addAnimation:animationGroup forKey:#"pulse"];
[CATransaction commit];
}
#end
Thanx in advance!

In regards to your actual question you likely need to set your CAAnimationGroup's fillMode property to kCAFillModeForward. The default value is kCAFillModeRemoved, so you are likely glimpsing the non-presentation layer between calls back and forth from your Down and Up methods.
That said, it appears that what you're trying to create is a cyclic animation through back and forth calls between your Up and Down methods via CATransaction's completionBlock. This seems an incredibly inefficient approach; why not try something more like:
-(void)scaleAndOpacityAnimations
{
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale.xy”];
scaleAnimation.fromValue = #(0.7f);
scaleAnimation.toValue = #(1.0f);
CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:#“opacity”];
opacityAnimation.fromValue = #(1.0f);
opacityAnimation.toValue = #(0.6f);
CAAnimationGroup *scaleAndOpacity = [CAAnimationGroup animation];
scaleAndOpacity.animations = #[scaleAnimation, opacityAnimation];
scaleAndOpacity.autoReverses = YES;
scaleAndOpacity.repeatCount = HUGE_VALF;
//OP had no value indicated for the duration in posted code, I’m including it here for completion
scaleAndOpacity.duration = 0.5f;
[self addAnimation:scaleAndOpacity forKey:#“pulse”];
}
Which will simply repeat infinitely many times without having to instantiate new CAAnimations on each cycle.

Related

CATextLayer foregroundColor animation don't work on iOS9 but work on iOS10 or high

CATextLayer foregroundColor animation don't work on iOS9 but work normal on iOS10 or high version! I don't know what's wrong with my code!
Code as follow:
- (void)addAnimationOnLayer:(CATextLayer*)layer{
CABasicAnimation *colorAn = [CABasicAnimation animationWithKeyPath:#"foregroundColor"];
colorAn.fromValue = (id)NeColor333333.CGColor;
colorAn.toValue = (id)NeColor999999.CGColor;
CABasicAnimation *sizeAn = [CABasicAnimation animationWithKeyPath:#"fontSize"];
sizeAn.fromValue = #16.0;
sizeAn.toValue = #12.0;
CAKeyframeAnimation *keyAn = [CAKeyframeAnimation animationWithKeyPath:#"position"];
keyAn.values = #[[NSValue valueWithCGPoint:layer.position],[NSValue valueWithCGPoint:CGPointMake(layer.position.x, layer.position.y - 16)]];
keyAn.keyTimes =#[#0.0,#1.0];
CAAnimationGroup *group = [CAAnimationGroup animation];
group.animations = #[colorAn, keyAn, sizeAn];
group.duration = 0.4;
group.repeatCount = 1;
group.fillMode = kCAFillModeForwards;
group.removedOnCompletion = NO;
[layer addAnimation:group forKey:nil];
}
You can add a CALayer, and set CALayer mask is a CATextLayer, you an change CALayer backgroundColor with animation.

Core Animation - animationDidStop not called

there.
in iOS app, Core animation callback don't work.
- (void)startAnim {
CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
anim.fromValue = startAngle;
anim.toValue = endAngle;
anim.duration = 2;
anim.delegate = self;
[self.target addAnimation:anim forKey:nil]; // self.target is CALayer instance, it's sublayer of Custom UIView
}
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag {
[self.target setValue:#(endAngle) forKeyPath:#"transform.rotation.z"];
}
But animationDidStop never be called.
If I change the code like as following, completion blocked is called.
- (void)startAnim {
CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
anim.fromValue = startAngle;
anim.toValue = endAngle;
anim.duration = 2;
anim.delegate = self;
[CATransaction begin];
[CATransaction setCompletionBlock:^{
[self.target setValue:#(endAngle) forKeyPath:#"transform.rotation.z"];
}];
[self.target addAnimation:anim forKey:nil];
[CATransaction commit];
}
But I don't want to use CATransaction.
Why is not animationDidStop called?
Update:
There is a way to set final value like as
- (void)startAnim {
CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
anim.fromValue = startAngle;
anim.toValue = endAngle;
anim.duration = 2;
anim.delegate = self;
[self.target setValue:#(endAngle) forKeyPath:#"transform.rotation.z"];
[self.target addAnimation:anim forKey:nil];
}
But final assignment should be done when the animation is finished. Because there are multiple dynamic animations of layer, so I don't know final value.
I found the reason why animationDidStop is not called.
Because animation was added in loop of other thread,
So I fixed like as following.
- (void)startAnim {
dispatch_async(dispatch_get_main_queue(), ^{
CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
anim.fromValue = startAngle;
anim.toValue = endAngle;
anim.duration = 2;
anim.delegate = self;
[self.target addAnimation:anim forKey:nil];
});
}
To me it sounds like you dont want the animation to reset its position, this is quite simple and achieved with a couple lines of code when setting up the animation.
It can be easily placed in your code like such:
anim.fillMode = kCAFillModeForwards;
anim.removedOnCompletion = NO;
What this means is when your animation has finished it will remain at the end and any further animations will be from that state.
Last year I use animation of UIView instead CABasicAnimation, but if my memory does not fail you have to set endAngle befor add animation, so try this:
- (void)startAnim {
CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
anim.fromValue = startAngle;
anim.toValue = endAngle;
anim.duration = 2;
anim.delegate = self;
[self.target setValue:#(endAngle) forKeyPath:#"transform.rotation.z"];
[self.target addAnimation:anim forKey:nil];
}
UPD:
You are setting anim.toValue = endAngle; before start animation, so its not good that end value changes after animation complete. Anyway you can set it again in animationDidStop
- (void)startAnim {
CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
anim.fromValue = startAngle;
anim.toValue = endAngle;
anim.duration = 2;
anim.delegate = self;
intermediateAngle = endAngle;
[self.target setValue:#(endAngle) forKeyPath:#"transform.rotation.z"];
[self.target addAnimation:anim forKey:nil];
}
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag {
if (intermediateAngle != endAngle) {
startAngle = intermediateAngle;
[self startAnim]; // or just [self.target setValue:#(endAngle) forKeyPath:#"transform.rotation.z"];
}
}

Can I rotate UIImage around the centre with animation?

I want to rotate image in circular animation. How can I do it in a simple way?
I know it is not difficult but I'm new to iOS.
Can anybody tell me right code ?
its my solution:
- (void)startAnimation
{
[UIView animateWithDuration:.4 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
[self.loadingRingImageView setTransform:CGAffineTransformRotate(self.loadingRingImageView.transform, M_SQRT2)];
} completion:^(BOOL finished) {
if (finished)
{
[self startAnimation];
}
}];
}
[[self.<imageviewinstance> layer] addAnimation:[self rotationAnimation] forKey:nil];
Add the animation as above and the below is the animation code.
- (CABasicAnimation *)rotationAnimation
{
CABasicAnimation *rotAnim = [CABasicAnimation animationWithKeyPath:#"position"];
[rotAnim setFromValue:[NSValue valueWithCGPoint:CGPointMake(100.0, 400.0)]];
rotAnim.toValue = [NSValue valueWithCGPoint:CGPointMake(300.0, 150.0)];
// rotAnim.duration = 5.0;
rotAnim.autoreverses = YES;
// rotAnim.repeatCount = HUGE_VAL;
rotAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
return rotAnim;
}
if the above is not worked use this
- (CABasicAnimation *)rotationAnimation
{
CABasicAnimation *move = [CABasicAnimation animationWithKeyPath:#"transform.rotation" ];
move.delegate = self;
[move setFromValue:[NSNumber numberWithFloat:0]];
[move setToValue:[NSNumber numberWithFloat:50]];
[move setDuration:10.0f];
move.autoreverses = YES;
}
the answer of question i was wondering is ->
UIImageView *imageViewForAnimation = [[UIImageView alloc] initWithImage:imageToAnimate];
imageViewForAnimation.alpha = 1.0f;
CGRect imageFrame = imageViewForAnimation.frame;
//Your image frame.origin from where the animation need to get start
CGPoint viewOrigin = imageViewForAnimation.frame.origin;
viewOrigin.y = viewOrigin.y + imageFrame.size.height / 2.0f;
viewOrigin.x = viewOrigin.x + imageFrame.size.width / 2.0f;
imageViewForAnimation.frame = imageFrame;
imageViewForAnimation.layer.position = viewOrigin;
[self.view addSubview:imageViewForAnimation];
// Set up fade out effect
CABasicAnimation *fadeOutAnimation = [CABasicAnimation animationWithKeyPath:#"opacity"];
[fadeOutAnimation setToValue:[NSNumber numberWithFloat:0.3]];
fadeOutAnimation.fillMode = kCAFillModeForwards;
fadeOutAnimation.removedOnCompletion = NO;
// Set up scaling
CABasicAnimation *resizeAnimation = [CABasicAnimation animationWithKeyPath:#"bounds.size"];
[resizeAnimation setToValue:[NSValue valueWithCGSize:CGSizeMake(40.0f, imageFrame.size.height * (40.0f / imageFrame.size.width))]];
resizeAnimation.fillMode = kCAFillModeForwards;
resizeAnimation.removedOnCompletion = NO;
// Set up path movement
CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
pathAnimation.calculationMode = kCAAnimationPaced;
pathAnimation.fillMode = kCAFillModeForwards;
pathAnimation.removedOnCompletion = NO;
//Setting Endpoint of the animation
CGPoint endPoint = CGPointMake(480.0f - 30.0f, 40.0f);
//to end animation in last tab use
//CGPoint endPoint = CGPointMake( 320-40.0f, 480.0f);
CGMutablePathRef curvedPath = CGPathCreateMutable();
CGPathMoveToPoint(curvedPath, NULL, viewOrigin.x, viewOrigin.y);
CGPathAddCurveToPoint(curvedPath, NULL, endPoint.x, viewOrigin.y, endPoint.x, viewOrigin.y, endPoint.x, endPoint.y);
pathAnimation.path = curvedPath;
CGPathRelease(curvedPath);
CAAnimationGroup *group = [CAAnimationGroup animation];
group.fillMode = kCAFillModeForwards;
group.removedOnCompletion = NO;
[group setAnimations:[NSArray arrayWithObjects:fadeOutAnimation, pathAnimation, resizeAnimation, nil]];
group.duration = 0.7f;
group.delegate = self;
[group setValue:imageViewForAnimation forKey:#"imageViewBeingAnimated"];
[imageViewForAnimation.layer addAnimation:group forKey:#"savingAnimation"];

How to easily use standard variants timing functions

I am wondering if there is a way to quickly use more different timing functions across an app. For instance using Robert Penner's, many toolkits expose them and I am surprised Apple doesn't.
Basically a way to have a block based command as flexible as animateWithDuration:delay:options:animations:completion: in UIView.
I come up with this solution and it would be great to know if there are better alternatives.
// UIView+CustomTimingFunction.h
#import <UIKit/UIKit.h>
typedef enum {
CustomTimingFunctionDefault,
CustomTimingFunctionLinear,
CustomTimingFunctionEaseIn,
CustomTimingFunctionEaseOut,
....
} CustomTimingFunction;
#interface UIView (CustomTimingFunction)
- (void)animateProperty:(NSString *)property from:(id)from to:(id)to withDuration:(NSTimeInterval)duration customTimingFunction:(CustomTimingFunction)customTimingFunction competion:(void (^)(void))competion;
#end
and
// UIView+CustomTimingFunction.m
#import "UIView+CustomTimingFunction.h"
#implementation UIView (CustomTimingFunction)
- (void)animateProperty:(NSString *)property from:(id)from to:(id)to withDuration:(NSTimeInterval)duration customTimingFunction:(CustomTimingFunction)customTimingFunction competion:(void (^)(void))competion
{
[CATransaction begin]; {
[CATransaction setCompletionBlock:competion];
if([property isEqualToString:#"frame"]){
CGRect fromRect = [from CGRectValue];
CGPoint fromPosition = CGPointMake(CGRectGetMidX(fromRect), CGRectGetMidY(fromRect));
CGRect toRect = [to CGRectValue];
CGPoint toPosition = CGPointMake(CGRectGetMidX(toRect), CGRectGetMidY(toRect));
CABasicAnimation *positionAnimation = [CABasicAnimation animationWithKeyPath:#"position"];
positionAnimation.timingFunction = [[self class] functionWithType:customTimingFunction];
positionAnimation.fromValue = [NSValue valueWithCGPoint:fromPosition];
positionAnimation.toValue = [NSValue valueWithCGPoint:toPosition];
CABasicAnimation *boundsAnimation = [CABasicAnimation animationWithKeyPath:#"bounds"];
boundsAnimation.timingFunction = [[self class] functionWithType:customTimingFunction];
boundsAnimation.fromValue = from;
boundsAnimation.toValue = to;
CAAnimationGroup * group =[CAAnimationGroup animation];
group.removedOnCompletion=NO;
group.fillMode=kCAFillModeForwards;
group.animations = #[positionAnimation,boundsAnimation];
group.duration = duration;
[self.layer addAnimation:group forKey:property];
}else{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:property];
animation.duration = duration;
animation.timingFunction = [[self class] functionWithType:customTimingFunction];
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
animation.fromValue = from;
animation.toValue = to;
[self.layer addAnimation:animation forKey:animation.keyPath];
}
} [CATransaction commit];
}
+(CAMediaTimingFunction *)functionWithType:(CustomTimingFunction)type
{
switch (type) {
case CustomTimingFunctionDefault:
return [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];
break;
case CustomTimingFunctionLinear:
return [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
break;
case CustomTimingFunctionEaseIn:
return [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
break;
case CustomTimingFunctionEaseOut:
return [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
break;
case CustomTimingFunctionEaseInOut:
return [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
break;
....
}
NSLog(#"Couldn't find CustomTimingFunction, will return linear");
return [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
}
#end
Thanks!

Loop animation - doesn't go

I'm trying to repeat a sequence of animations, inside a LOOP, changing at each loop some parameters randomly. Here is the code. Anyone please knows why it doesn't work?
If I call it once with a button action, it works, but with a loop it doesn't.
Thanks a lot! Giuseppe
-(IBAction)startLoop:(id)sender {
for (int i=1;i<10; i++) {
[self animation2];
}
}
-(id) animation2 {
int max=500;
UIImage *myImage = [UIImage imageNamed:#"coccinella2.png"];
CALayer *myLayer = [CALayer layer];
myLayer.contents = (id)myImage.CGImage;
myLayer.bounds = CGRectMake(0, 0, 50, 60);
[myLayer setPosition:CGPointMake(arc4random()%(max), arc4random()%(max))];
[myLayer setBounds:CGRectMake(0.0, 0.0, 50.0, 60.0)];
[self.view.layer addSublayer:myLayer];
//translation1
CGPoint startPt = CGPointMake(arc4random()%(max),arc4random()%(max));
CGPoint endPt = CGPointMake(arc4random()%(max),arc4random()%(max));
CABasicAnimation *transl1 = [CABasicAnimation animationWithKeyPath:#"position"];
transl1.removedOnCompletion = FALSE;
transl1.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
transl1.fromValue = [NSValue valueWithCGPoint:startPt];
transl1.toValue = [NSValue valueWithCGPoint:endPt];
transl1.duration = 2.0;
transl1.fillMode = kCAFillModeForwards;
transl1.beginTime = 0;
//scale 1
CABasicAnimation *scale1 = [CABasicAnimation
animationWithKeyPath:#"transform.scale"];
scale1.removedOnCompletion = FALSE;
[scale1 setToValue:[NSNumber numberWithInt:3]];
[scale1 setDuration:2.0f];
scale1.fillMode = kCAFillModeForwards;
scale1.beginTime = 0;
//rotation1
CABasicAnimation *rotation1 = [CABasicAnimation
animationWithKeyPath:#"transform.rotation.z"];
rotation1.removedOnCompletion = FALSE;
[rotation1 setFromValue:DegreesToNumber(0)];
[rotation1 setToValue:DegreesToNumber(90)];
//rotation1.repeatCount = HUGE_VALF;
[rotation1 setDuration:2.0f];
rotation1.fillMode = kCAFillModeForwards;
rotation1.beginTime = 0;
//group
CAAnimationGroup* group = [CAAnimationGroup animation];
[group setDuration: 6.0];
group.removedOnCompletion = FALSE;
group.fillMode = kCAFillModeForwards;
[group setAnimations: [NSArray arrayWithObjects:scale1, transl1, rotation1, nil]];
[myLayer addAnimation: group forKey: nil];
}
Your code doesn't repeat the annotation 10 times but starts 10 animations right away. If your goal is to start the animations after the previous one ended you should try using an NSTimer.
You can use an NSTimer to trigger each group of animations at a different time, or you can set the beginTime on each group, with code like this:
group.beginTime = CACurrentMediaTime() + delay;
For animations, using the beginTime gives more accurate timing, since CA is run on a separate thread and doesn't stall like NSTimers if your app gets busy.

Resources