IOS Animation not working with my animation block - ios

Im Trying to make a red square move from top of view to a random position at the bottom of the view and the return to the top again at random position.
The app starts with the following code into [super viewLoad]:
[redSquare setCenter:CGPointMake(25,25)];
(with the red square view set 50 x 50, this starts the app with the redsquare in the top left corner).
The first part of the animation works well - the red square animates to a random position to the bottom of the page. But, it then jumps to a random position to the top without animation.
Please help what I'm doing wrong or any other solutions to help with this simple animation.
-(void)moving{
[UIView animateWithDuration:1.0 delay:0.0 options:UIViewAnimationCurveEaseIn animations:^{
int randomx = arc4random() % 295;
[redSquare setCenter:CGPointMake(randomx,435)];
} completion:^(BOOL finished) {
[redSquare setCenter:CGPointMake(redSquare.frame.origin.x,25)];
}];
}

Your completion block simply sets the new position. You aren't animating it back to the top:
Try this, instead
-(void)moving {
[UIView animateWithDuration:1.0 delay:0.0 options:UIViewAnimationCurveEaseIn animations:^{
int randomx = arc4random() % 295;
[redSquare setCenter:CGPointMake(randomx,435)];
} completion:^(BOOL finished) {
[UIViewAnimateWithDuration:1.0 delay:0.0 options:UIViewAnimationCurveEaseIn animations:^{
[redSquare setCenter:CGPointMake(redSquare.frame.origin.x,25)];
} completion: NULL
}];
}
Edited to add
It's true that sublevels can get confusing, but you can make life simple and less indented if you define the blocks separately from the method.
For example, you could rewrite the above as:
-(void)moving {
workBlk_t animationBlock = ^ {
int randomx = arc4random() % 295;
[redSquare setCenter:CGPointMake(randomx,435)];
};
void (^completionBlock)(BOOL finished) = ^{
[UIViewAnimateWithDuration:1.0 delay:0.0 options:UIViewAnimationCurveEaseIn animations:^{
[redSquare setCenter:CGPointMake(redSquare.frame.origin.x,25)];
} completion : NULL
};
[UIView animateWithDuration:1.0
delay:0.0
options:UIViewAnimationCurveEaseIn
animations:animationBlock
completion:completionBlock
}];
}

you should start new animation in completion block for example:
completion:^(BOOL finished) {
[UIView animateWithDuration:1.0 delay:0.0 options:UIViewAnimationCurveEaseIn animations:^{
int randomx = arc4random() % 295;
[redSquare setCenter:CGPointMake(randomx,25)];
} completion:^(BOOL finished) {
}];
}];

This part does exactly what you are describing:
completion:^(BOOL finished) {
[redSquare setCenter:CGPointMake(redSquare.frame.origin.x,25)];
}
I.e. returns the object to the top of the screen after the animiation is completed. This will not be animated, it will happen after the animation is completed, and thus it will be instant.
You need to trigger a new animation after the animation is completed.
To create several nested animation blocks can however become a bit messy, the most clean way is to create another method for animating back and call it when the first animation is completed, like this:
-(void) moveDown{
[UIView animateWithDuration:1.0 delay:0.0 options:UIViewAnimationCurveEaseIn animations:^{
int randomx = arc4random() % 295;
[redSquare setCenter:CGPointMake(randomx,435)];
} completion:^(BOOL finished) {
// First animation completed
[self moveBack];
}];
}
-(void) moveBack{
[UIView animateWithDuration:1.0 delay:0.0 options:UIViewAnimationCurveEaseIn animations:^{
[redSquare setCenter:CGPointMake(redSquare.frame.origin.x,25)];
} completion:^(BOOL finished) {
// Second animation completed
}];
}

Related

How to prevent 2 Animation on Object at the same time in Objective-C?

I have a UILabel and each time it touched, it'll scaled with CGAffineTransformMakeScale and animation, then back to real size with animation. Now if it touched before last animation ends, it'll scale the scaled object not the real size of main object and so it'll scaled more than it should to be scale. How to prevent this problem ? I want to second touch before last scale ending scale the main size of object.
Here is My Code :
(void)MyLabelTouched
{
[UIView animateWithDuration:.15 animations:^{
MyLabel.transform = CGAffineTransformMakeScale(1.5,1.5);
} completion:^(BOOL finished) {
[UIView animateWithDuration:.15 animations:^{
MyLabel.transform = CGAffineTransformIdentity;
}];
}];
}
Try this
(void)MyLabelTouched
{
if(animRunning){return;}
animRunning = YES;
[ MyLabel.layer removeAllAnimations];
MyLabel.transform = CGAffineTransformIdentity;
[UIView animateWithDuration:.15 animations:^{
MyLabel.transform = CGAffineTransformMakeScale(1.5,1.5);
} completion:^(BOOL finished) {
[UIView animateWithDuration:.15 animations:^{
MyLabel.transform = CGAffineTransformIdentity;
animRunning = NO;
}];
}];
}

How to repeat shake animation indefinitely or X times using animateWithDuration?

The code below came from this SO question: UIView shake animation.
This shake animation is perfect, except it stops after one iteration. How do you repeat the animation indefinitely, or how do you cause it to repeat it X times?
There are other SO answers on repeating UIView animations that suggest using CAKeyframeAnimation or CABasicAnimation, but this question is different. The goal is to reproduce this exact animation, with the "springy" effect from usingSpringWithDamping and initialSpringVelocity.
Using Autoreverse and Repeat don't reproduce the desired effect because the the initial translation is outside the animation block.
view.transform = CGAffineTransformMakeTranslation(20, 0);
[UIView animateWithDuration:0.4 delay:0.0 usingSpringWithDamping:0.2 initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
view.transform = CGAffineTransformIdentity;
} completion:nil];
If you need exact same code few times than put that code in method and do recursion, something like that:
- (void)animateCount:(NSInteger)count {
if (count == 0) {
return;
}
view.transform = CGAffineTransformMakeTranslation(20, 0);
[UIView animateWithDuration:0.4 delay:0.0 usingSpringWithDamping:0.2 initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
view.transform = CGAffineTransformIdentity;
} completion:^{
count--;
[self animateCount:count];
}];
}
Use (UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat) in options to repeat it indefinitely
[UIView animateWithDuration:0.4 delay:0.0 usingSpringWithDamping:0.2 initialSpringVelocity:1.0 options:(UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat | UIViewAnimationOptionCurveEaseInOut) animations:^{
someView.frame = someFrame1;
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.5f animations:^{
someView.frame = someFrame2;
} completion:nil];
}];

How to autorepeat a set of chained UIView animations using UIViewAnimationOptionRepeat

I am trying to chain a set of UIView animateWithDuration: using the flag UIViewAnimationOptionRepeat
The animation steps I am trying to repeat are:
Animate a UIView 100 pixels to the left
Fade the UIView out (opacity '0')
While faded out (invisible), move it back to the original position (100 px to the right)
Fade the UIView back in (set opacity to '1')
Repeat the animation using flag UIViewAnimationOptionRepeat
I am not able to repeat the whole chain of animations - and only able to repeat the top most animation.
The code I have tried is this:
[UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionRepeat animations:^{
self.handImageView.frame = CGRectMoveByXPixels(self.handImageView.frame, -100);
[UIView animateWithDuration:0.5 delay:1 options:0 animations:^{
self.handImageView.alpha = 0;
} completion:^(BOOL finished) {
self.handImageView.frame = CGRectMoveByXPixels(self.handImageView.frame, 100);
[UIView animateWithDuration:0.5 delay:0 options:0 animations:^{
self.handImageView.alpha = 1;
} completion:^(BOOL finished) {
}];
}];
} completion:^(BOOL finished) {
}];
The problem is that an animation that uses UIViewAnimationOptionRepeat never finishes and therefore never calls the completion block. However, you can repeat the animations using the performSelector method, as shown in the code below.
- (void)repeatingAnimationForView:(UIView *)view
{
[UIView animateWithDuration:1 delay:0 options:0
animations:^{ view.frame = CGRectMoveByXPixels(view.frame, -100); }
completion:^(BOOL finished) { if ( finished ) {
[UIView animateWithDuration:0.5 delay:0 options:0
animations:^{ view.alpha = 0; }
completion:^(BOOL finished) { if ( finished ) {
view.frame = CGRectMoveByXPixels(view.frame, 100);
[UIView animateWithDuration:0.5 delay:0 options:0
animations:^{ view.alpha = 1; }
completion:^(BOOL finished) { if ( finished ) {
if ( self.enableRepeatingAnimation )
[self performSelector:#selector(repeatingAnimationForView:) withObject:view afterDelay:0];
}}]; }}]; }}];
}
- (void)stopRepeatingAnimationForView:(UIView *)view
{
self.enableRepeatingAnimation = NO;
[view.layer removeAllAnimations];
view.frame = CGRectMake( 100, 137, 80, 50 );
view.alpha = 1;
}
- (void)startRepeatingAnimationForView:(UIView *)view
{
self.enableRepeatingAnimation = YES;
[self repeatingAnimationForView:view];
}
To stop the animations immediately, call the stopRepeatingAnimationForView method as shown above. To stop the animations at the end of a cycle, simply set self.enableRepeatingAnimation to NO.

When creating an animation to cause a UIImageView to go up and down (almost float) infinitely, how do I stop a pause at the end of the animation?

Here's the code I'm using to take a UIImageView and make it float up and down.
[UIView animateKeyframesWithDuration:2.0 delay:0.0 options:UIViewKeyframeAnimationOptionRepeat animations:^{
[UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:0.25 animations:^{
self.slider.transform = CGAffineTransformMakeTranslation(0, -5.0);
}];
[UIView addKeyframeWithRelativeStartTime:0.25 relativeDuration:0.5 animations:^{
self.slider.transform = CGAffineTransformMakeTranslation(0, 5.0);
}];
[UIView addKeyframeWithRelativeStartTime:0.75 relativeDuration:0.25 animations:^{
self.slider.transform = CGAffineTransformMakeTranslation(0, 0.0);
}];
} completion:^(BOOL finished) {
}];
However, it comes out looking like this with this delay after the animation ends and before it restarts.
How do I make it fluid?
I'm not sure what effect you're going for exactly, but I think this gives you something like your code does without the delay. I usually do this by animating a constraint, rather than using transforms. In this example, I've made an IBOutlet (topCon) to the constraint to the top of the view:
-(IBAction)floatView:(id)sender {
static int i= 1;
static float duration = .25;
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
self.topCon.constant = self.topCon.constant - (5 * i);
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
i = (i == 1 || i == 2)? -2 : 2;
duration = 0.5;
[self floatView:self];
}];
}

Fade in and fade out a UIView while it is moving

It's easy enough to animate the view:
[UIView animateWithDuration:1.0
animations:^{theView.center = newCenter; theView.alpha = 0;}
completion:^(BOOL finished){
[theView removeFromSuperview];
}];
The problem is that when I add it as a subview, I want it to fade in and already look like it is moving. Right now it appears immediately, then moves and fades out.
So, I need to set it's initial alpha to zero, fade it quickly while it is moving, then fade it out. Is this possible with UIView animations? I can't have two competing animation blocks working on the same object right?
All you need to do is apply 2 animations back-to-back. Something like this ::
theView.alpha = 0;
[UIView animateWithDuration:1.0
animations:^{
theView.center = midCenter;
theView.alpha = 1;
}
completion:^(BOOL finished){
[UIView animateWithDuration:1.0
animations:^{
theView.center = endCenter;
theView.alpha = 0;
}
completion:^(BOOL finished){
[theView removeFromSuperview];
}];
}];
So in the 1st second it will appear while moving and then in the next second it will fade out
Hope this helps
Put the initial alpha=0 outside animation block.
theView.alpha = 0;
[UIView animateWithDuration:1.0
animations:^{
theView.center = newCenter;
theView.alpha = 1;
}
completion:^(BOOL finished){
// Do other things
}];

Resources