iOS animation with duration and autoreverse - ios

I am animating an UIImageView with the UIView block API. My animation fades the UIImageView In and Out continuously. How can I animate this only for a specific time duration ?
I have put up this piece of code
float tempDuration = 5.0;
[UIView animateWithDuration:tempDuration
delay:0
options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionAllowUserInteraction
animations:^{
_imageView.alpha = 1;
}
completion:nil];

First of all allow me to introduce an article that is awesome to understand some features of the animations:
Controlling Animation Timming
In this article you have a part that shows what you want.
You want something like this:
Therefore you can configure that in the following way:
-(void) animateImageView:(UIImageView*) imageView
withDuration:(int) duration
withRepeatCount: (int) repeatCount {
CABasicAnimation *opacityAnim = [CABasicAnimation animationWithKeyPath:#"opacity"];
opacityAnim.fromValue = #1.0;
opacityAnim.toValue = #0.0;
opacityAnim.autoreverses = YES;
opacityAnim.duration = duration/2.0/repeatCount;
opacityAnim.removedOnCompletion = YES;
opacityAnim.repeatCount = repeatCount;
[imageView.layer addAnimation:opacityAnim forKey:nil];
}
This way you guarantee that your animation will always be 5 seconds and you can tune in the number of repetitions.

Maybe you could try CABasicAnimation instead if [UIView animateWithDiration: ... ]
it should be something like this:
CABasicAnimation *opacityAnim = [CABasicAnimation animationWithKeyPath:#"opacity"];
opacityAnim.fromValue = [NSNumber numberWithFloat:1.0];
opacityAnim.toValue = [NSNumber numberWithFloat:0.0];
opacityAnim.autoreverses = YES;
opacityAnim.duration = duration;
opacityAnim.removedOnCompletion = YES;
opacityAnim.repeatCount = 5;
[imageView.layer addAnimation:opacityAnim forKey:nil];

You can do the following changes to do your work.
- (void)animateImageView{
static int animcount = 1;
float tempDuration = 1.0;
[UIView animateWithDuration:tempDuration
delay:0
options: UIViewAnimationOptionAutoreverse
animations:^{
self.imgView.alpha = 1;
}
completion:^(BOOL finished) {
if(finished && animcount<5){
animcount+=1;
[self animateImageView];
}
else if (finished && animcount==5){
[self.imgView.layer removeAllAnimations];
}
}];
}

Related

"Glitch" during UIView Intro CAAnimation

I'm trying to add an Animation to a UIView. The objective of the animation is to "animate" the View appearing on screen (instead of just appearing there).
The animation is all size scaling: start from 5%, go up to 120% and then very quickly back to 100% of the regular scale.
My issue is that the full scale UIView appears very quickly, before the animation starts.
Here's the code:
UIView * myView = [[UIView alloc] initWithFrame:someFrame];
[self.view addSubview:myView];
[self initialAnimationFor:myView];
-(void) initialAnimationFor:(UIView*)pView {
const CFTimeInterval firstDuration = 0.75f;
const CFTimeInterval secondDuration = 0.025f;
const float initialValue = 0.05f;
const float middleValue = 1.20f;
CABasicAnimation * firstAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
firstAnimation.duration = firstDuration;
firstAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
firstAnimation.fromValue = [NSNumber numberWithFloat:initialValue];
firstAnimation.toValue = [NSNumber numberWithFloat:middleValue];
CABasicAnimation * secondAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
secondAnimation.duration = secondDuration;
secondAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
secondAnimation.fromValue = [NSNumber numberWithFloat:middleValue];
secondAnimation.toValue = [NSNumber numberWithFloat:1.0f];
CAAnimationGroup *animationGroup = [CAAnimationGroup new];
animationGroup.duration = firstDuration + secondDuration;
animationGroup.animations = #[firstAnimation, secondAnimation];
[pView.layer addAnimation:animationGroup forKey:nil];
}
Any ideas? Thanks!
I would do a different technique and use chained UIView block animations, like this:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(40, 40, 200, 200)];
myView.backgroundColor = [UIColor redColor];
[self initialAnimationFor:myView];
}
- (void)initialAnimationFor:(UIView*)pView {
pView.transform = CGAffineTransformMakeScale(0.05f, 0.05f);
if (pView.superview == nil) {
[self.view addSubview:pView];
}
[UIView
animateWithDuration:0.75f
animations:^{
pView.transform = CGAffineTransformMakeScale(1.20f, 1.20f);
}
completion:^(BOOL finished) {
[UIView
animateWithDuration:0.25f // <-- Your old value of 0.025f makes the animation VERY quick
animations:^{
pView.transform = CGAffineTransformIdentity;
}
];
}
];
}
With this setup, you get the "grow to slightly larger than 100% and then 'settle' to 100%" effect.
Is this a workable solution?

CABasicAnimation back to its original position after it finish it's 1 cycle

Don't refer CABasicAnimation returns to the original position before the next animation and Objective-C - CABasicAnimation applying changes after animation? and CABasicAnimation rotate returns to original position
i have tried.
below code does,bottom->top->goes left->back to it’s original position.
bottom->top->goes left->back to its original position.
I need bottom->top->goes left.bottom->top->goes left so on…
-(void)addUpDownAnimationForButton:(UILabel*)label
{
CABasicAnimation * bottomAnimation ;
bottomAnimation =[CABasicAnimation animationWithKeyPath:#"transform.translation.y"];
[bottomAnimation setValue:#"animation1" forKey:#"id"];
bottomAnimation.delegate = self;
[UIView setAnimationDidStopSelector:#selector(animationDidStop:finished:)];
bottomAnimation.duration = 2.0;
bottomAnimation.fromValue = [NSNumber numberWithFloat:13];
bottomAnimation.toValue = [NSNumber numberWithFloat:-7];
bottomAnimation.repeatCount = 0;
bottomAnimation.fillMode = kCAFillModeForwards;
bottomAnimation.removedOnCompletion = NO;
[btnSpecialForListing.titleLabel.layer addAnimation:bottomAnimation forKey:#"transform.translation.y"];
}
- (void)animationDidStop:(CAAnimation *)theAnimation2 finished:(BOOL)flag
{
if([[theAnimation2 valueForKey:#"id"] isEqual:#"animation1"]) {
CABasicAnimation *moveAnimation;
moveAnimation=[CABasicAnimation animationWithKeyPath:#"transform.translation.x"];
moveAnimation.duration=3.5;
moveAnimation.repeatCount=0;
moveAnimation.autoreverses=NO;
moveAnimation.fromValue=[NSNumber numberWithFloat:0];
moveAnimation.toValue=[NSNumber numberWithFloat:-400];
moveAnimation.removedOnCompletion = NO;
moveAnimation.fillMode = kCAFillModeRemoved;
[btnSpecialForListing.titleLabel.layer addAnimation:moveAnimation forKey:#"transform.translation.x"];
}
}
Any help would be greatly appreciated.
Thanks !!
Have you tried this?
moveAnimation.fillMode = kCAFillModeForwards;
moveAnimation.removedOnCompletion = NO;
EDIT
As far as I can see you can also use animateWithDuration which has a completion block.
Example would be:
CGRect rect = btnSpecialForListing.titleLabel.frame;
[UIView animateWithDuration:2.0 animations:^{
rect.origin.y = -7;
btnSpecialForListing.titleLabel.frame = rect;
} completion:^(BOOL finished){
[UIView animateWithDuration:3.5 animation:^{
rect.origin.x = -400;
btnSpecialForListing.titleLabel.frame = rect;
}];
}];
Swift 4.2/5:
moveAnimation.fillMode = .forwards
moveAnimation.isRemovedOnCompletion = false

Converting code from CABasicAnimation

I have a animation code that is written using CABasicAnimation just as following:
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"position"];
[anim setDelegate:self];
anim.fromValue = [NSValue valueWithCGPoint:img.center];
if (newValue == 0) {
imgCentre.y = centerStart.y - 11 * kCounterDigitDiff;
anim.toValue = [NSValue valueWithCGPoint:imgCentre];
} else
anim.toValue = [NSValue valueWithCGPoint:imgCentre];
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
anim.duration = 0.3;
[img.layer addAnimation:anim forKey:#"rollLeft"];
img.frame = frame;
Due to some reason I wish to apply same animation but by using following type of approach:
[UIView animateWithDuration:0.3 delay:0.0 options:(UIViewAnimationOptionBeginFromCurrentState|UIViewAnimationOptionCurveEaseOut) animations:^{
...
} completion:^(BOOL finished) {
...
}
What I come up with is like following:
[img setCenter:imgCentre];
[UIView animateWithDuration:0.3 delay:0.0 options:(UIViewAnimationOptionBeginFromCurrentState|UIViewAnimationOptionCurveEaseOut) animations:^{
if (newValue == 0) {
CGPoint newPoint= CGPointMake(imgCentre.x, centerStart.y - 11 * kCounterDigitDiff);
[img setCenter:newPoint];
} else{
[img setCenter:imgCentre];
}
} completion:^(BOOL finished) {
// animation completion code
}];
img.frame = frame;
Overall things look good but I am a little bit pessimistic about the conversion? is there something that i have missed?
You've got the right idea. However, unlike with CAAnimations, you don't have to set the properties to the end value. The UIView animation call takes care of all that for you.
UIView animation is generally easier and more intuitive to use than CAAnimation.

Spin and move UIView

How to move continuously spinning UIView with animation blocks? Eg., I have this code for spinning:
- (void)spinView:(UIView *)view {
[UIView animateWithDuration:0.2f
delay:0.0f
options:UIViewAnimationOptionCurveLinear
animations:^{
view.transform = CGAffineTransformRotate(view.transform, M_PI_2);
}
completion:^(BOOL finished) {
if (finished) {
[self spinView:view];
}
}];
}
How to combine it with animation like this:
[UIView animateWithDuration:4.0f animations:^{
myView.transform = CGAffineTransformTranslate(myView.transform, 400, 0);
}];
I've tried with CGAffineTransformConcat(), UIViewAnimationOptionBeginFromCurrentState, but without success.
Have you try something like,
[UIView animateWithDuration:2.0f animations:^{
vw.transform = CGAffineTransformTranslate(vw.transform, 400, 0);
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
animation.fromValue = [NSNumber numberWithFloat:0.0f];
animation.toValue = [NSNumber numberWithFloat: 2*M_PI];
animation.duration = 5.0f;
animation.repeatCount = 1;
[vw.layer addAnimation:animation forKey:#"SpinAnimation"];
}];
vw is your view to move. set frame according to your wish.

Pause and Resume "Line" growth animation

I have tried so many ways but could not do it smoothly, I am a kind a newB in animations any help would be appreciated.
Code I Tried:
I have the buttons and the labels in need to add in two arrays. eventBtnsPopUpArray and eventLabelsPopUpArray. I try to add them in a loop and the animation is not smooth.
if ([eventBtnsPopUpArray count]>0)
{
CABasicAnimation* scalingAnimation;
scalingAnimation = [CABasicAnimation animationWithKeyPath:#"frame.size"];;
scalingAnimation.fromValue = [NSNumber numberWithFloat:0];
scalingAnimation.toValue = [NSNumber numberWithFloat:1024];
scalingAnimation.duration = 5.0;
scalingAnimation.cumulative = YES;
scalingAnimation.removedOnCompletion = NO;
scalingAnimation.fillMode = kCAFillModeForwards;
[calendarEventView.layer addAnimation:scalingAnimation forKey:#"rotationAnimation"];
for (int k=0; k<[eventBtnsPopUpArray count]; k++)
{
UIButton *btn = [eventBtnsPopUpArray objectAtIndex:0];
UILabel *lbl = [eventLabelsPopUpArray objectAtIndex:0];
[UIView animateWithDuration:2
delay:0
options: UIViewAnimationCurveEaseOut
animations:^{
[self addSubview:btn];
[self addSubview:lbl];
}
completion:^(BOOL finished){
[self pauseLayer:calendarEventView.layer];
[self resumeLayer:calendarEventView.layer];
}
];
}
if (calendarEventView.frame.size.width < 1024) {
[UIView beginAnimations:#"DrawLineTillEnd" context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
calendarEventView.frame = CGRectMake(0, 65, 1024, 7);
[UIView commitAnimations];
}
}
//Pause and resume layer functions
-(void)pauseLayer:(CALayer*)layer
{
CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
layer.speed = 0.0;
layer.timeOffset = pausedTime;
}
-(void)resumeLayer:(CALayer*)layer
{
CFTimeInterval pausedTime = [layer timeOffset];
layer.speed = 1.0;
layer.timeOffset = 0.0;
layer.beginTime = 0.0;
CFTimeInterval timeSincePause =
[layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
layer.beginTime = timeSincePause;
}
I don't think you need to use core-animation, just use an NSTimer and adjust the frame (width) of the UIView using a changing variable... A simple conditional statement (if-statement) checking the width can stop it at a certain point and add the first desired UIButton. When clicked this button will change a BOOLEAN which will trigger the code in the NSTimer to continue line-growth until it reaches the next width defined by the next if-statement. etc.
I dont know how you can do this using coreplot or coreanimation but doing this is really simple by view animation
for example
-(void)animateLine{
UILabel *labelObj = [[UiLabel alloc]init];
//Set background image and height of label say 100 pixel
for(int incrementer = 0; incrementer<100; incrementer ++)
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.01];
[labelObj setFrame:cgRectMake(0,100,incrementer,20)];
[uiview commitAnimation];
}
}

Resources