63

Here is some code I struggle with for a while.

If you start the fade in animation, the label text fades in. If I start the fade out animation the the label text fades out.

When I start the startFade method, only fade out is shown. How can I wait for the fadeIn method to finish visually before starting the fadeOut method.

-(IBAction)startFade:(id)sender{
    [self fadeIn];
    [self fadeOut];
}

-(IBAction)fadeIn:(id)sender{
    [self fadeIn];
}

-(IBAction)fadeOut:(id)sender{
[self fadeOut];
}

-(void) fadeIn{
    [_label setAlpha:0];
    [UILabel beginAnimations:NULL context:nil];
    [UILabel setAnimationDuration:2.0];
    [_label setAlpha:1];
    [UILabel commitAnimations];
}

-(void) fadeOut{
    [UILabel beginAnimations:NULL context:nil];
    [UILabel setAnimationDuration:2.0];
    [_label setAlpha:0];
    [UILabel commitAnimations];
}
ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
SoTm
  • 807
  • 1
  • 7
  • 10

13 Answers13

124

When you call the fadeIn and fadeOut methods back to back like you're doing, the code is run instantaneously, so you'll only see animation from the last method called. UIView block based animation provides a completion handler, which seems to be exactly what you're looking for. So your code might looks something like this:

-(IBAction)startFade:(id)sender {

    [_label setAlpha:0.0f];        

    //fade in
    [UIView animateWithDuration:2.0f animations:^{

        [_label setAlpha:1.0f];

    } completion:^(BOOL finished) {

        //fade out
        [UIView animateWithDuration:2.0f animations:^{

            [_label setAlpha:0.0f];

        } completion:nil];

    }];
}

Swift:

@IBAction func startFade(_ sender: AnyObject) {

    label.alpha = 0.0

    // fade in
    UIView.animate(withDuration: 2.0, animations: { 
        label.alpha = 1.0
    }) { (finished) in
        // fade out
        UIView.animate(withDuration: 2.0, animations: {
            label.alpha = 0.0
        })
    }
}
hgwhittle
  • 9,316
  • 6
  • 48
  • 60
34

that does the job for you (the _label is your label);

- (IBAction)startFade:(id)sender {
    [_label setAlpha:0.f];

    [UIView animateWithDuration:2.f delay:0.f options:UIViewAnimationOptionCurveEaseIn animations:^{
        [_label setAlpha:1.f];
    } completion:^(BOOL finished) {
        [UIView animateWithDuration:2.f delay:0.f options:UIViewAnimationOptionCurveEaseInOut animations:^{
            [_label setAlpha:0.f];
        } completion:nil];
    }];
}
holex
  • 23,961
  • 7
  • 62
  • 76
  • 5
    Can be simplified by adding UIViewAnimationOptionAutoReverse to the options of the first animation, i.e. UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionAutoReverse, and just having completion:nil instead of another animation. – Bjørn Egil May 22 '14 at 19:19
  • @BjørnEgil, if there is no such a goal which is about changing any attribute of the `UILabel` between half way, that could also work as well. – holex May 22 '14 at 21:08
  • yeah, but that was not part of the question. It was just how to fade in and out. – Bjørn Egil May 22 '14 at 21:17
  • 1
    @BjørnEgil, the answer tries to be as generic as it is possible for another users or developers, who may try to re-use the idea. – holex May 22 '14 at 21:40
7

Generic answer : You can use this method to apply animation to any UIView object . First create an extension of UIView class . Create a separate swift file and write the code like this

import Foundation
import UIKit

extension UIView {

    func fadeIn() {
        //Swift 2
        UIView.animateWithDuration(1.0, delay: 0.0, options: UIViewAnimationOptions.CurveEaseIn, animations: {
            self.alpha = 1.0
        }, completion: nil)

        //Swift 3, 4, 5
        UIView.animate(withDuration: 1.0, delay: 0.0, options: UIView.AnimationOptions.curveEaseIn, animations: {
            self.alpha = 1.0
        }, completion: nil)
    }


    func fadeOut() {
        //Swift 2
        UIView.animateWithDuration(1.0, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
            self.alpha = 0.0
        }, completion: nil)

        //Swift 3, 4, 5
        UIView.animate(withDuration: 1.0, delay: 0.0, options: UIView.AnimationOptions.curveEaseOut, animations: {
            self.alpha = 0.0
        }, completion: nil)
    }


}

Here self refers to any UIView you refer to . You can use buttons, labels etc to call these 2 methods .

Then in any other swift class you can call fadeIn() and fadeOut() like this :

self.yourUIObject.fadeIn()
self.yourUIObject.fadeOut()

This gives the desired effect of animation to any UIObject .

Karen Hovhannisyan
  • 1,140
  • 2
  • 21
  • 31
iPhoneDeveloper
  • 958
  • 1
  • 14
  • 23
6

Try this..

// Fade Out

 -(void)fadeOut:(UIView*)viewToDissolve withDuration:(NSTimeInterval)duration   andWait:(NSTimeInterval)wait
{
[UIView beginAnimations: @"Fade Out" context:nil];

// wait for time before begin
[UIView setAnimationDelay:wait];

// druation of animation
[UIView setAnimationDuration:duration];
viewToDissolve.alpha = 0.0;
[UIView commitAnimations];
}

// Fade In

-(void) fadeIn:(UIView*)viewToFadeIn withDuration:(NSTimeInterval)duration andWait:(NSTimeInterval)wait

{
[UIView beginAnimations: @"Fade In" context:nil];

// wait for time before begin
[UIView setAnimationDelay:wait];

    // druation of animation
[UIView setAnimationDuration:duration];
viewToFadeIn.alpha = 1;
[UIView commitAnimations];

}

// Fade in from fade out

-(void) fadeInFromFadeOut: (UIView*)viewToFadeIn withDuration:(NSTimeInterval)duration
{
    viewToFadeIn.hidden=NO;
    [self fadeOut:viewToFadeIn withDuration:1 andWait:0];
    [self fadeIn:viewToFadeIn withDuration:duration andWait:0];

}

// Button operation

-(void) buttonClicked :(id)sender
{
   NSLog(@"Button clicked");

// Each button is given a tag
int tag = ((UIButton*)sender).tag;
if (tag ==1)
{
    sprite.alpha  =1;
    [self fadeOut : sprite withDuration: 10 andWait : 1 ];
}
else if (tag ==2)
{
    sprite.alpha  =0;
    [self fadeIn : sprite withDuration: 3 andWait : 1 ];
}
else
{
    [self fadeInFromFadeOut:sprite withDuration:10];
}
}

View this link to download sample..

Refer this link.

Happy to share with you..:-)

yazh
  • 701
  • 10
  • 15
  • Cool Stuff;Everybody!! Checked out a lot of your Ideas and approaches. Helped me a lot!! – SoTm Aug 25 '14 at 11:36
2

you can do something like this (check possible parameters values and similar methods here : https://developer.apple.com/library/ios/documentation/uikit/reference/uiview_class/uiview/uiview.html

[UIView animateWithDuration:duration
                      delay:delay
                    options:option 
                 animations:^{
                     //fade in here (changing alpha of UILabel component)
                 } 
                 completion:^(BOOL finished){
                    if(finished){
                      //start a fade out here when previous animation is finished (changing alpha of UILabel component)
                 }];
}
Julian
  • 9,299
  • 5
  • 48
  • 65
2

Based on @holex's answer, but simplified a bit (as commented):

- (IBAction)startFade:(id)sender {
   [_label setAlpha:0.f];

   [UIView animateWithDuration:2.f 
                         delay:0.f 
                       options:UIViewAnimationOptionCurveEaseIn
                             | UIViewAnimationOptionAutoreverse 
                    animations:^{
                                  [_label setAlpha:1.f];
                                } 
                    completion:nil];
}
brynbodayle
  • 6,546
  • 2
  • 33
  • 49
Bjørn Egil
  • 2,398
  • 1
  • 20
  • 22
2

Swift 4 If you need just one pulse when clicking the button, use that:

@IBAction func didPressButton(_ sender: UIButton) {
        self.someView.alpha = 0
        UIView.animate(withDuration: 0.4,
                       delay: 0,
                       options: [.curveEaseInOut, .autoreverse],
                       animations: {
                        self.someView.alpha = 1
        },
                       completion: { _ in
                        self.someView.alpha = 0
        })
    }
Nike Kov
  • 12,630
  • 8
  • 75
  • 122
2

Swift 5 version of iPhoneDeveloper's answer:

extension UIView {
    
    func fadeIn() {
        UIView.animate(withDuration: 1.0, delay: 0.0, options: UIView.AnimationOptions.curveEaseIn, animations: {
            self.alpha = 1.0
        }, completion: nil)
    }
    
    func fadeOut() {
        UIView.animate(withDuration: 1.0, delay: 0.0, options: UIView.AnimationOptions.curveEaseOut, animations: {
            self.alpha = 0.0
        }, completion: nil)
    }
}
craft
  • 2,017
  • 1
  • 21
  • 30
jglasse
  • 1,188
  • 12
  • 23
1

The easiest way would be to use:

[UIView animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion]

and add the fadeOut call to the completion block. The documentation might help answer any questions you have.

If you can't use the block version for some reason, then you'll have to set a delegate ([UIView setAnimationDelegate:(id)delegate]) and a selector with ([UIView setAnimationDidStopSelector:]) that the delegate will respond to.

Again, see the documentation for more details.

jemmons
  • 18,605
  • 8
  • 55
  • 84
1

My task was to make a label fade out. And then fade in with changed text. The solution was:

    -(void)performAnimationOnHistoryButtonLabelUpdatingTextTo:(NSString *)text
{
    [UIView animateWithDuration:0.4f animations:^{
        [self.historyButtonLabel setAlpha:0.0f];

    } completion:^(BOOL finished) {
        self.historyButtonLabel.text = text;

        [UIView animateWithDuration:0.4f animations:^{
            [self.historyButtonLabel setAlpha:1.0f];
        } completion:nil];

    }];
}
Naloiko Eugene
  • 2,453
  • 1
  • 28
  • 18
1
labelAnimate = (UILabel*) [self.view viewWithTag:101];
btnTapMe = (UIButton*) [self.view viewWithTag:100];
[btnTapMe addTarget:self action:@selector(startAnimating:) forControlEvents:UIControlEventTouchUpInside];

//////////////

-(void) startAnimating:(UIButton*)button {
    [labelAnimate setAlpha:0.0];
    [NSTimer scheduledTimerWithTimeInterval:1.8 target:self selector:@selector(continuousEaseInOut) userInfo:button repeats:YES];
}

-(void) continuousFadeInOut {
    [UIView animateWithDuration:2.0 delay:0.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
        [labelAnimate setAlpha:1.0];
    } completion:^(BOOL finished) {
        [UIView animateWithDuration:2.0 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
            [labelAnimate setAlpha:0.0];
        } completion:nil];
    }];
}
Dovydas Šopa
  • 2,282
  • 8
  • 26
  • 34
1

I strongly suggest you use a generic implementation so you can reuse the code whenever you need the fade effect again.

You should create an UIView extension:

UIView+Animations.h

#import <Foundation/Foundation.h>

@interface UIView (Animations)

- (void)fadeInWithCompletion:(void (^ __nullable)(BOOL finished))completion;
- (void)fadeOutWithCompletion:(void (^ __nullable)(BOOL finished))completion;;

@end

UIView+Animations.m

#import "UIView+Animations.h"

@implementation UIView (Animations)

- (void)fadeInWithCompletion:(void (^ __nullable)(BOOL finished))completion {
    [UIView animateWithDuration:2 animations:^{
        [self setAlpha:1];
    } completion:completion];
}

- (void)fadeOutWithCompletion:(void (^ __nullable)(BOOL finished))completion {
    [UIView animateWithDuration:2 animations:^{
        [self setAlpha:0];
    } completion:completion];
}

@end

So, you only have to import the new file to your class or inside your Prefix.pch and use it like this:

[_label fadeOutWithCompletion:^(BOOL finished) {
   [_label fadeInWithCompletion:nil];
}];

Note that you could also use nil as the completion parameter when you have nothing else to do after completion.

I also recommend you do not parameterize the duration to keep a pattern through you entire application.

This implementation can be used in the future for UIButton, UILabel, UITextField... Well, any class derived from UIView.

joao.arruda
  • 367
  • 2
  • 13
1

The fade in and fade out animations can be combined using UIView.animate(withDuration: animations:)

UIView.animate(withDuration: animationDuration, animations: {
            myView.alpha = 0.75
            myView.alpha = 1.0
        })
ArunGJ
  • 2,685
  • 21
  • 27