How to autorepeat a set of chained UIView animations using UIViewAnimationOptionRepeat - ios

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.

Related

iOS continuous fade-out and fade-in animation

In objective-c, I want to add fade in and fade out animation to a UIView. I want to do the fade-out and fade-in animation continuously. To illustrate it, what effect I want to have is as following:
fade out - fade in - fade out - fade in - ...
What I'm trying is as following:
-(void)fadeOutIn:(UIView *)view duration:(NSTimeInterval)duration
{
static CGFloat alpha = 1.0;
[UIView animateWithDuration:duration
delay:0.0
options: UIViewAnimationOptionRepeat| UIViewAnimationOptionCurveEaseInOut
animations:^{
alpha = abs((int)(alpha - 1.0));
view.alpha = alpha;
}
completion:^(BOOL finished){
}];
}
I also tried following:
-(void)fadeOutIn:(UIView *)view duration:(NSTimeInterval)duration
{
[UIView animateWithDuration:duration
delay:0.0
options: UIViewAnimationOptionRepeat| UIViewAnimationOptionCurveEaseInOut
animations:^{
view.alpha = 0.0;
}
completion:^(BOOL finished){
[UIView animateWithDuration:duration
delay:0.0
options: UIViewAnimationOptionRepeat|UIViewAnimationOptionCurveEaseInOut
animations:^{
view.alpha = 1.0;
}
completion:^(BOOL finished){
}];
}];
}
But the issue is they worked all the same, i.e., the UIView will fade out properly and SUDDENLY show up(not fade in), then fade out again...
Seems the fade in animation not work at all. Does anybody know what's wrong of my code please? Any suggestion will be highly appreciated.
Try this
[UIView animateKeyframesWithDuration:1 delay:0 options:UIViewAnimationOptionRepeat|UIViewAnimationOptionAutoreverse|UIViewAnimationCurveEaseInOut animations:^{
self.customIV.alpha = 0;
} completion:^(BOOL finished) {
}];
Here's this, Vigor, this does work, but I had to modify the code, this is somewhat dangerous code, due to it's constant recursiion, but you'll get the point:
-(void)fadeOutIn
{
[UIView animateWithDuration:1
delay:0.0
options: UIViewAnimationOptionCurveEaseInOut
animations:^{
[[self contentView] emailField].alpha = 0.0;
}
completion:^(BOOL finished){
[UIView animateWithDuration:1
delay:0.0
options: UIViewAnimationOptionCurveEaseInOut
animations:^{
[[self contentView] emailField].alpha = 1.0;
}
completion:^(BOOL finished){
[self fadeOutIn];
}];
}];
}
I would really rethink how this works, I used it on a button in my view and it will recurse constanly, until the view is removed from the superview. I had to hardcode the button in the code.
[UIView animateWithDuration:duration
delay:0.0
options: UIViewAnimationOptionRepeat| UIViewAnimationOptionAutoreverse|UIViewAnimationOptionCurveEaseInOut
animations:^{
view.alpha = 0.0;
}
completion:nil];
Just call this once
Try this....
YourView.alpha = 1;// set the initial value of alpha
[UIView animateWithDuration:1 delay:0
options: UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat
animations:^{
YourView.alpha = 0; // set the max value of alpha
} completion:nil];
This solution is for Swift 3,4 and 5
UIView.animateKeyframes(withDuration: 1, delay: 0, options: [UIView.KeyframeAnimationOptions(rawValue: UIView.AnimationOptions.repeat.rawValue), UIView.KeyframeAnimationOptions(rawValue: UIView.KeyframeAnimationOptions.RawValue(UIView.AnimationCurve.easeInOut.rawValue)), UIView.KeyframeAnimationOptions(rawValue: UIView.AnimationOptions.autoreverse.rawValue)] , animations: {
self.logoCenterImageView.alpha = 0
}) { finished in
}

Animation of UIButton decreases its size

Here is a small clip of a view being animated by me:
Animation flash
I am having problems when animation from ? to S and vice versa. I am setting the appropriate frames to go off screen, and then back on from the other size. Why does the size of the black button decrease with every move around it?
-(void)moveToRight
{
//just made this method up, but my actual code
//special case
if (currentDay == 7) {
//move off the screen to the right
CGRect offScreenRightFrame = CGRectMake(self.circle.frame.origin.x + 60, self.circle.frame.origin.y, self.circle.frame.size.width, self.circle.frame.size.height);
//move to the left of the screen so we can animate it in
CGRect offScreenLeftFrame = CGRectMake(-40, self.circle.frame.origin.y, self.circle.frame.size.width, self.circle.frame.size.height);
if (self.delegate) {
if ([self.delegate respondsToSelector:#selector(dayChangedTo:)]) [self.delegate dayChangedTo:[self getCurrentDay]];
}
[self pulseCircle];
[UIView animateWithDuration:0.25f delay:0.0f options:UIViewAnimationOptionCurveLinear animations:^{
self.circle.frame = offScreenRightFrame;
}completion:^(BOOL finished) {
if (finished) {
self.circle.alpha = 0.0f;
self.circle.frame = offScreenLeftFrame;
[UIView animateWithDuration:0.25f delay:0.0f options:UIViewAnimationOptionCurveLinear animations:^{
self.circle.center = self.labelSun.center;
self.circle.alpha = 1.0f;
}completion:^(BOOL finished) {
if (finished) {
[UIView animateWithDuration:0.25f animations:^{
[self changeColors];
[self pulseCircle];
}];
}
}];
}
}];
}
-(void)moveLeft
{
if (currentDay == 8) {
CGRect circleFrame = self.circle.frame;
CGRect offScreenLeft = CGRectOffset(circleFrame, -20, 0);
CGRect offScreenRightFrame = CGRectMake(self.labelQuestion.frame.origin.x + 30, self.labelQuestion.frame.origin.y, circleFrame.size.width, circleFrame.size.height);
[self pulseCircle];
[UIView animateWithDuration:0.25f delay:0.0f options:UIViewAnimationOptionCurveLinear animations:^{
self.circle.frame = frame;
}completion:^(BOOL finished) {
if (finished) {
self.circle.alpha = 0.0f;
self.circle.frame = frameFinal;
[UIView animateWithDuration:0.25f delay:0.0f options:UIViewAnimationOptionCurveLinear animations:^{
self.circle.center = self.labelQuestion.center;
self.circle.alpha = 1.0f;
}completion:^(BOOL finished) {
if (finished) {
[UIView animateWithDuration:0.25f animations:^{
[self changeColors];
[self pulseCircle];
}];
}
}];
}
}];
}
- (void)pulseCircle
{
__block UILabel *day = [self getLabelOfDay];
[UIView animateWithDuration:TRANLATE_DURATION/2 delay:0.0f options:UIViewAnimationOptionBeginFromCurrentState animations:^{
self.circle.layer.transform = CATransform3DMakeScale(1.35f, 1.35f, 1);
day.transform = CGAffineTransformMakeScale(1.35f, 1.35f);
}completion:^(BOOL finished) {
[UIView animateWithDuration:TRANLATE_DURATION/2 animations:^{
self.circle.layer.transform = CATransform3DIdentity;
day.transform = CGAffineTransformIdentity;
}];
}];
}
I just put my logic.. honestly i don't read/see your code/logic but may be my idea work for you ;)
So, I think.. you should add size of circle in public variable as default size. And set this size whenever you rich at.
if (currentDay == 0 || currentDay == 8)
{
// Here you need to set your default size.
}
May be my logic work for you, Because 1 to 7 day its work fine but issue created at when currentDay rich at 0 or 8 day.
Thanks :)

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

UIView animation: simulating velocity

I am trying to use a UIView animation to simply move a view onto the screen:
[UIView animateWithDuration:.10 delay:0 options: nil animations:^
{
self.menusView.frame = endingMenuViewFrame;;
}
completion:^(BOOL finished)
{
}];
I'm wanting to add an animation so that UIView floats a little when it reaches the top before it comes down, i.e. akin to if someone jumps in the air - when they first jump, they shoot up quickly, but then gravity gradually slows them down as they reach the top of their jump, and eventually it pushes them back to earth. Does anyone know how to accomplish this?
Try with this code. This will make your view bounce three times with each time your height reduced by half. You may add more bounces.
CGFloat offset = 200.0;
CGRect originalFrame = self.menusView.frame;
[UIView animateWithDuration:1.0 animations:^{
CGRect frame = self.runnerAlertView.frame;
frame.origin.y = frame.origin.y - offset;
self.menusView.frame = frame;
} completion:^(BOOL finished){
[UIView animateWithDuration:1.0 animations:^{
self.menusView.frame = originalFrame;
} completion:^(BOOL finished) {
[UIView animateWithDuration:1.0 animations:^{
CGRect frame = self.menusView.frame;
frame.origin.y = frame.origin.y - 0.5 *offset;
self.menusView.frame = frame;
} completion:^(BOOL finished){
[UIView animateWithDuration:1.0 animations:^{
self.menusView.frame = originalFrame;
} completion:^(BOOL finished) {
[UIView animateWithDuration:1.0 animations:^{
CGRect frame = self.runnerAlertView.frame;
frame.origin.y = frame.origin.y - 0.25 * offset;
self.menusView.frame = frame;
} completion:^(BOOL finished){
[UIView animateWithDuration:1.0 animations:^{
self.menusView.frame = originalFrame;
}];
}];
}];
}];
}];
}];

IOS Animation not working with my animation block

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

Resources