UIView Hide / Show avec animation


154

Mon objectif simple est de fondre les fonctions de masquage et d'affichage animées.

Button.hidden = YES;

Assez simple. Cependant, est-il possible de le faire disparaître plutôt que de simplement disparaître? Cela semble plutôt peu professionnel de cette façon.

Réponses:


259

Dans iOS 4 et versions ultérieures, il existe un moyen de le faire en utilisant simplement la méthode de transition UIView sans avoir besoin d'importer QuartzCore. Vous pouvez simplement dire:

Objectif c

[UIView transitionWithView:button
                  duration:0.4
                   options:UIViewAnimationOptionTransitionCrossDissolve
                animations:^{
                     button.hidden = YES;
                }
                completion:NULL];

Rapide

UIView.transition(with: button, duration: 0.4, 
                  options: .transitionCrossDissolve, 
                  animations: {
                 button.hidden = false
              })

Solution précédente

La solution de Michail fonctionnera, mais ce n'est pas vraiment la meilleure approche.

Le problème avec le fondu alpha est que parfois les différentes couches de vue qui se chevauchent semblent étranges lorsqu'elles disparaissent. Il existe d'autres alternatives utilisant Core Animation. Incluez d'abord le framework QuartzCore dans votre application et ajoutez-le #import <QuartzCore/QuartzCore.h>à votre en-tête. Vous pouvez maintenant effectuer l'une des opérations suivantes:

1) définissez button.layer.shouldRasterize = YES;puis utilisez le code d'animation alpha que Michail a fourni dans sa réponse. Cela empêchera les calques de se mélanger de manière étrange, mais entraînera une légère pénalité en termes de performances et peut rendre le bouton flou s'il n'est pas aligné exactement sur une limite de pixel.

Alternativement:

2) Utilisez le code suivant pour animer le fondu à la place:

CATransition *animation = [CATransition animation];
animation.type = kCATransitionFade;
animation.duration = 0.4;
[button.layer addAnimation:animation forKey:nil];

button.hidden = YES;

La bonne chose à propos de cette approche est que vous pouvez fondre toutes les propriétés du bouton même si elles ne peuvent pas être animées (par exemple, le texte ou l'image du bouton), configurez simplement la transition et définissez vos propriétés immédiatement après.


5
@robmathers, je viens de tester votre code, au-dessus de deux codes fonctionnent juste quand button.hidden = NO, pour un fondu en situation; n'ont aucun effet d'animation pour le fondu quand button.hidden = YES;
Jason

Semble être cassé dans iOS 12.0
user3532505

5
Vous devez utiliser la supervision de l'élément que vous animez comme transitionWithViewparamètre pour garantir un fondu entrant et sortant réussi.
allenh le

159

Les propriétés animées UIView sont:

- frame
- bounds
- center
- transform
- alpha
- backgroundColor
- contentStretch

Décrivez dans: Animations

isHidden n'est pas l'un d'entre eux, donc selon moi, le meilleur moyen est:

Swift 4:

func setView(view: UIView, hidden: Bool) {
    UIView.transition(with: view, duration: 0.5, options: .transitionCrossDissolve, animations: {
        view.isHidden = hidden
    })
}

Objectif c:

- (void)setView:(UIView*)view hidden:(BOOL)hidden {
    [UIView transitionWithView:view duration:0.5 options:UIViewAnimationOptionTransitionCrossDissolve animations:^(void){
        [view setHidden:hidden];
    } completion:nil];
}

8
En fait, c'est la réponse simple et meilleure
Irshad Mohamed

3
Bien que cela s'anime correctement, l'UISearchBar que j'essaie d'afficher apparaît au mauvais endroit jusqu'à ce que l'animation se termine, puis saute instantanément à la bonne position. Une idée? J'utilise des Storyboards avec Interface Builder et Contraintes.
Greg Hilston

5
Ce code ne fonctionne pas ... il change directement l'état sans animation
Mihir Mehta

2
@evya ne fonctionne que pour le fondu d'entrée lorsqu'il est masqué = NON , Ne fonctionne pas pour le fondu, masqué = OUI
Jason

Impressionnant. 10x beaucoup
Vyacheslav

125

Pour disparaître:

Objectif c

[UIView animateWithDuration:0.3 animations:^{
    button.alpha = 0;
} completion: ^(BOOL finished) {//creates a variable (BOOL) called "finished" that is set to *YES* when animation IS completed.
    button.hidden = finished;//if animation is finished ("finished" == *YES*), then hidden = "finished" ... (aka hidden = *YES*)
}];

Swift 2

UIView.animateWithDuration(0.3, animations: {
    button.alpha = 0
}) { (finished) in
    button.hidden = finished
}

Swift 3, 4, 5

UIView.animate(withDuration: 0.3, animations: {
    button.alpha = 0
}) { (finished) in
    button.isHidden = finished
}

Pour s'estomper:

Objectif c

button.alpha = 0;
button.hidden = NO;
[UIView animateWithDuration:0.3 animations:^{
    button.alpha = 1;
}];

Swift 2

button.alpha = 0
button.hidden = false
UIView.animateWithDuration(0.3) {
    button.alpha = 1
}

Swift 3, 4, 5

button.alpha = 0
button.isHidden = false
UIView.animate(withDuration: 0.3) {
    button.alpha = 1
}

l'utilisation du fondu in / out en conjonction avec l'état caché a résolu mon problème
ACLima

Pour une raison quelconque, animer sur hidden = YES fonctionnait bien pour moi, mais animer sur hidden = NO ne faisait rien, donc cette combinaison d'animation de l'alpha et de définition de la propriété cachée était utile.
arlomedia

Je viens d'écrire une démo, mais seulement caché = NON, fondu dans les œuvres, étrange
Jason

9

J'utilise cette petite extension Swift 3 :

extension UIView {

  func fadeIn(duration: TimeInterval = 0.5,
              delay: TimeInterval = 0.0,
              completion: @escaping ((Bool) -> Void) = {(finished: Bool) -> Void in }) {
    UIView.animate(withDuration: duration,
                   delay: delay,
                   options: UIViewAnimationOptions.curveEaseIn,
                   animations: {
      self.alpha = 1.0
    }, completion: completion)
  }

  func fadeOut(duration: TimeInterval = 0.5,
               delay: TimeInterval = 0.0,
               completion: @escaping (Bool) -> Void = {(finished: Bool) -> Void in }) {
    UIView.animate(withDuration: duration,
                   delay: delay,
                   options: UIViewAnimationOptions.curveEaseIn,
                   animations: {
      self.alpha = 0.0
    }, completion: completion)
  }
}

7

Swift 3

func appearView() {
     self.myView.alpha = 0
     self.myView.isHidden = false

     UIView.animate(withDuration: 0.9, animations: {
         self.myView.alpha = 1
     }, completion: {
         finished in
         self.myView.isHidden = false
     })
}

7

rapide 4.2

avec extension:

extension UIView {
func hideWithAnimation(hidden: Bool) {
        UIView.transition(with: self, duration: 0.5, options: .transitionCrossDissolve, animations: {
            self.isHidden = hidden
        })
    }
}

méthode simple:

func setView(view: UIView, hidden: Bool) {
    UIView.transition(with: view, duration: 0.5, options: .transitionCrossDissolve, animations: {
        view.isHidden = hidden
    })
}

Comment puis-je ajouter un délai pour celui-ci?
cvdogan

7

Utilisez cette solution pour des effets de fondu et de fondu en douceur

extension UIView {
    func fadeIn(duration: TimeInterval = 0.5, delay: TimeInterval = 0.0, completion: @escaping ((Bool) -> Void) = {(finished: Bool) -> Void in }) {
        self.alpha = 0.0

        UIView.animate(withDuration: duration, delay: delay, options: UIView.AnimationOptions.curveEaseIn, animations: {
            self.isHidden = false
            self.alpha = 1.0
        }, completion: completion)
    }

    func fadeOut(duration: TimeInterval = 0.5, delay: TimeInterval = 0.0, completion: @escaping (Bool) -> Void = {(finished: Bool) -> Void in }) {
        self.alpha = 1.0

        UIView.animate(withDuration: duration, delay: delay, options: UIView.AnimationOptions.curveEaseOut, animations: {
            self.isHidden = true
            self.alpha = 0.0
        }, completion: completion)
    }
}

l'utilisation est comme

uielement.fadeIn()
uielement.fadeOut()

Merci


fadeOutfonctionne sur iOS 13 uniquement si je supprime les lignes définies self.isHidden.
Mike Taverne

6

J'ai créé pour la catégorie UIViewà cet effet et mis en œuvre un peu spécial bit concept différent: visibility. La principale différence de ma solution est que vous pouvez appeler [view setVisible:NO animated:YES]et juste après cette vérification synchrone [view visible]et obtenir un résultat correct. C'est assez simple mais extrêmement utile.

De plus, il est permis d'éviter d'utiliser la "logique booléenne négative" (voir Code complet, page 279, Utiliser des noms de variables booléennes positifs pour plus d'informations).

Rapide

UIView+Visibility.swift

import UIKit


private let UIViewVisibilityShowAnimationKey = "UIViewVisibilityShowAnimationKey"
private let UIViewVisibilityHideAnimationKey = "UIViewVisibilityHideAnimationKey"


private class UIViewAnimationDelegate: NSObject {
    weak var view: UIView?

    dynamic override func animationDidStop(animation: CAAnimation, finished: Bool) {
        guard let view = self.view where finished else {
            return
        }

        view.hidden = !view.visible
        view.removeVisibilityAnimations()
    }
}


extension UIView {

    private func removeVisibilityAnimations() {
        self.layer.removeAnimationForKey(UIViewVisibilityShowAnimationKey)
        self.layer.removeAnimationForKey(UIViewVisibilityHideAnimationKey)
    }

    var visible: Bool {
        get {
            return !self.hidden && self.layer.animationForKey(UIViewVisibilityHideAnimationKey) == nil
        }

        set {
            let visible = newValue

            guard self.visible != visible else {
                return
            }

            let animated = UIView.areAnimationsEnabled()

            self.removeVisibilityAnimations()

            guard animated else {
                self.hidden = !visible
                return
            }

            self.hidden = false

            let delegate = UIViewAnimationDelegate()
            delegate.view = self

            let animation = CABasicAnimation(keyPath: "opacity")
            animation.fromValue = visible ? 0.0 : 1.0
            animation.toValue = visible ? 1.0 : 0.0
            animation.fillMode = kCAFillModeForwards
            animation.removedOnCompletion = false
            animation.delegate = delegate

            self.layer.addAnimation(animation, forKey: visible ? UIViewVisibilityShowAnimationKey : UIViewVisibilityHideAnimationKey)
        }
    }

    func setVisible(visible: Bool, animated: Bool) {
        let wereAnimationsEnabled = UIView.areAnimationsEnabled()

        if wereAnimationsEnabled != animated {
            UIView.setAnimationsEnabled(animated)
            defer { UIView.setAnimationsEnabled(!animated) }
        }

        self.visible = visible
    }

}

Objectif c

UIView+Visibility.h

#import <UIKit/UIKit.h>

@interface UIView (Visibility)

- (BOOL)visible;
- (void)setVisible:(BOOL)visible;
- (void)setVisible:(BOOL)visible animated:(BOOL)animated;

@end

UIView+Visibility.m

#import "UIView+Visibility.h"

NSString *const UIViewVisibilityAnimationKeyShow = @"UIViewVisibilityAnimationKeyShow";
NSString *const UIViewVisibilityAnimationKeyHide = @"UIViewVisibilityAnimationKeyHide";

@implementation UIView (Visibility)

- (BOOL)visible
{
    if (self.hidden || [self.layer animationForKey:UIViewVisibilityAnimationKeyHide]) {
        return NO;
    }

    return YES;
}

- (void)setVisible:(BOOL)visible
{
    [self setVisible:visible animated:NO];
}

- (void)setVisible:(BOOL)visible animated:(BOOL)animated
{
    if (self.visible == visible) {
        return;
    }

    [self.layer removeAnimationForKey:UIViewVisibilityAnimationKeyShow];
    [self.layer removeAnimationForKey:UIViewVisibilityAnimationKeyHide];

    if (!animated) {
        self.alpha = 1.f;
        self.hidden = !visible;
        return;
    }

    self.hidden = NO;

    CGFloat fromAlpha = visible ? 0.f : 1.f;
    CGFloat toAlpha = visible ? 1.f : 0.f;
    NSString *animationKey = visible ? UIViewVisibilityAnimationKeyShow : UIViewVisibilityAnimationKeyHide;

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
    animation.duration = 0.25;
    animation.fromValue = @(fromAlpha);
    animation.toValue = @(toAlpha);
    animation.delegate = self;
    animation.removedOnCompletion = NO;
    animation.fillMode = kCAFillModeForwards;
    [self.layer addAnimation:animation forKey:animationKey];
}

#pragma mark - CAAnimationDelegate

- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)finished
{
    if ([[self.layer animationForKey:UIViewVisibilityAnimationKeyHide] isEqual:animation]) {
        self.hidden = YES;
    }
}

@end

La version de @valentin Shergin Swift arrive?
Juan Boero

Sûr! J'ai ajouté la version Swift.
Valentin Shergin

5

le code de @Umair Afzal fonctionne bien dans Swift 5 après quelques changements

 extension UIView {

func fadeIn(duration: TimeInterval = 0.5, delay: TimeInterval = 0.0, completion: @escaping ((Bool) -> Void) = {(finished: Bool) -> Void in }) {
    self.alpha = 0.0

    UIView.animate(withDuration: duration, delay: delay, options: UIView.AnimationOptions.curveEaseIn, animations: {
        self.isHidden = false
        self.alpha = 1.0
    }, completion: completion)
}

func fadeOut(duration: TimeInterval = 0.5, delay: TimeInterval = 0.0, completion: @escaping (Bool) -> Void = {(finished: Bool) -> Void in }) {
    self.alpha = 1.0

    UIView.animate(withDuration: duration, delay: delay, options: UIView.AnimationOptions.curveEaseIn, animations: {
        self.alpha = 0.0
    }) { (completed) in
        self.isHidden = true
        completion(true)
    }
  }
}

pour utilisation

yourView.fadeOut()
yourView.fadeIn()

donnant un effet dur pendant le fondu, a ajouté une meilleure solution ici
Dhanu K

4

Swift 4

extension UIView {

func fadeIn(duration: TimeInterval = 0.5, delay: TimeInterval = 0.0, completion: @escaping ((Bool) -> Void) = {(finished: Bool) -> Void in }) {
    self.alpha = 0.0

    UIView.animate(withDuration: duration, delay: delay, options: UIViewAnimationOptions.curveEaseIn, animations: {
        self.isHidden = false
        self.alpha = 1.0
    }, completion: completion)
}

func fadeOut(duration: TimeInterval = 0.5, delay: TimeInterval = 0.0, completion: @escaping (Bool) -> Void = {(finished: Bool) -> Void in }) {
    self.alpha = 1.0

    UIView.animate(withDuration: duration, delay: delay, options: UIViewAnimationOptions.curveEaseIn, animations: {
        self.alpha = 0.0
    }) { (completed) in
        self.isHidden = true
        completion(true)
    }
}
}

Et pour l'utiliser, appelez simplement ces fonctions comme:

yourView.fadeOut() // this will hide your view with animation
yourView.fadeIn() /// this will show your view with animation

Vous venez de copier la réponse de @ MarkMckelvie
Ashley Mills

Il y a une différence, il ne cachait pas la vue. Et j'avais aussi besoin de cacher la vue. Alors fais ça et partage-le.
Umair Afzal

3
Pourquoi ne pas simplement commenter l'autre réponse au lieu de la copier et de la faire passer pour la vôtre?
Ashley Mills

2

isHidden est une valeur immédiate et vous ne pouvez pas affecter une animation dessus, au lieu de cela, vous pouvez utiliser Alpha pour masquer votre vue

UIView.transition(with: view, duration: 0.5, options: .transitionCrossDissolve, animations: {
        view.alpha = 0
    })

Et pour montrer:

UIView.transition(with: view, duration: 0.5, options: .transitionCrossDissolve, animations: {
      view.alpha = 1
})

1

Vous pouvez le faire TRÈS facilement en utilisant la bibliothèque Animatics :

//To hide button:
AlphaAnimator(0) ~> button

//to show button
AlphaAnimator(1) ~> button

1
func flipViews(fromView: UIView, toView: UIView) {

    toView.frame.origin.y = 0

    self.view.isUserInteractionEnabled = false

    UIView.transition(from: fromView, to: toView, duration: 0.5, options: .transitionFlipFromLeft, completion: { finished in            

        fromView.frame.origin.y = -900

        self.view.isUserInteractionEnabled = true

    })


}

1

Vous pouvez essayer ceci.

 func showView(objView:UIView){

    objView.alpha = 0.0
    UIView.animate(withDuration: 0.5, animations: {
        objView.alpha = 0.0
    }, completion: { (completeFadein: Bool) -> Void in
        objView.alpha = 1.0
        let transition = CATransition()
        transition.duration = 0.5
        transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
        transition.type = kCATransitionFade
        objView.layer.add(transition, forKey: nil)
    })
}

func HideView(objView:UIView){

    UIView.animate(withDuration: 0.5, animations: {
        objView.alpha = 1.0
    }, completion: { (completeFadein: Bool) -> Void in
        objView.alpha = 0.0
        let transition = CATransition()
        transition.duration = 0.5
        transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
        transition.type = kCATransitionFade
        objView.layer.add(transition, forKey: nil)
    })
}

Et passez votre nom de vue

        showView(objView: self.viewSaveCard)
        HideView(objView: self.viewSaveCard)

1

Si votre vue est définie sur Caché par défaut ou si vous modifiez l'état Caché, ce que je pense que vous devriez dans de nombreux cas, aucune des approches de cette page ne vous donnera à la fois une animation FadeIn / FadeOut, elle n'animera qu'un de ces états, la raison en est que vous définissez l'état Caché sur false avant d'appeler UIView.animate méthode , ce qui provoquera une visibilité soudaine et si vous que l'alpha, l'espace objet est toujours là mais il n'est pas visible, ce qui entraînera des problèmes d'interface utilisateur.

La meilleure approche consiste donc à vérifier d'abord si la vue est masquée, puis à définir l'alpha sur 0,0, comme ceci lorsque vous définissez l'état Caché sur false, vous ne verrez pas une visibilité soudaine.

func hideViewWithFade(_ view: UIView) {
    if view.isHidden {
        view.alpha = 0.0
    }

    view.isHidden = false

    UIView.animate(withDuration: 0.3, delay: 0.0, options: .transitionCrossDissolve, animations: {
        view.alpha = view.alpha == 1.0 ? 0.0 : 1.0
    }, completion: { _ in
        view.isHidden = !Bool(truncating: view.alpha as NSNumber)
    })
}

Cela résout le problème que d'autres ont demandé sur les endroits où les fadeins ne fonctionnaient pas. Approche intelligente.
BooTooMany

1

La fonction UIView.transition (avec :) est agréable et soignée.

Beaucoup l'ont publié, mais aucun n'a remarqué qu'il y a une erreur qui n'apparaîtra que lorsque vous l'exécuterez.

Vous pouvez parfaitement faire passer la propriété cachée à true, tandis que lorsque vous tentez de la faire passer à false, la vue disparaîtra soudainement sans aucune animation.

En effet, cette API ne fonctionne que dans une vue, ce qui signifie que lorsque vous passez d'une vue à afficher, en fait elle-même s'affiche immédiatement, seul son contenu est animé progressivement.

Lorsque vous essayez de masquer cette vue, elle-même se cache tout de suite, rend l'animation à son contenu sans signification.

Pour résoudre ce problème, lorsque vous masquez une vue, la cible de la transition doit être sa vue parente au lieu de la vue que vous souhaitez masquer.

func transitionView(_ view: UIView?, show: Bool, completion: BoolFunc? = nil) {
    guard let view = view, view.isHidden == show, let parent = view.superview else { return }

    let target: UIView = show ? view : parent
    UIView.transition(with: target, duration: 0.4, options: [.transitionCrossDissolve], animations: {
        view.isHidden = !show
    }, completion: completion)
}

0

Ma solution pour Swift 3 . Donc, j'ai créé la fonction, qui cache / affiche la vue dans le bon ordre (lors du masquage - définissez alpha sur 0 puis isHidden sur true; unhiding - révélez d'abord la vue, puis définissez son alpha sur 1):

func hide(_ hide: Bool) {
    let animations = hide ? { self.alpha = 0 } :
                            { self.isHidden = false }
    let completion: (Bool) -> Void = hide ? { _ in self.isHidden = true } :
                                            { _ in UIView.animate(withDuration: duration, animations: { self.alpha = 1 }) }
    UIView.animate(withDuration: duration, animations: animations, completion: completion)
}

Pourquoi dans le completionbloc il y a une autre animation quand hideest faux?
Giorgio

0

Transition Swift 4

    UIView.transition(with: view, duration: 3, options: .transitionCurlDown,
                      animations: {
                        // Animations
                        view.isHidden = hidden
    },
                      completion: { finished in
                        // Compeleted
    })

Si vous utilisez l'approche pour les anciennes versions de Swift, vous obtiendrez une erreur:

Cannot convert value of type '(_) -> ()' to expected argument type '(() -> Void)?'

Référence utile .


cela fonctionne-t-il avec la mise en page automatique? un code similaire n'est pas animé. la isHiddenvaleur est rendue instantanément (c.-à-d. masquant / affichant instantanément la vue).
Crashalot

0

Ce code donne une animation comme pousser viewController dans le contrôleur uinavigation ...

CATransition *animation = [CATransition animation];
 animation.type = kCATransitionPush;
 animation.subtype = kCATransitionFromRight;
 animation.duration = 0.3;
 [_viewAccountName.layer addAnimation:animation forKey:nil];

 _viewAccountName.hidden = true;

Utilisé ceci pour l'animation pop ...

 CATransition *animation = [CATransition animation];
 animation.type = kCATransitionPush;
 animation.subtype = kCATransitionFromLeft;
 animation.duration = 0.3;
 [_viewAccountName.layer addAnimation:animation forKey:nil];

 _viewAccountName.hidden = false;

0

J'ai essayé certaines des réponses abandonnées, certaines ne fonctionnent que pour une situation, certaines d'entre elles doivent ajouter deux fonctions.

Option 1

Rien à voir avec view.isHidden.

extension UIView {
    func animate(fadeIn: Bool, withDuration: TimeInterval = 1.0) {
        UIView.animate(withDuration: withDuration, delay: 0.0, options: .curveEaseInOut, animations: {
            self.alpha = fadeIn ? 1.0 : 0.0
        })
    }
}

Puis passe isFadeIn( trueou false)

view.animate(fadeIn: isFadeIn) 

Option 2

Ne passez aucun paramètre. Il s'estompe ou disparaît selon isUserInteractionEnabled. Cela convient également très bien à la situation animée d'avant en arrière.

func animateFadeInOut(withDuration: TimeInterval = 1.0) {
    self.isUserInteractionEnabled = !self.isUserInteractionEnabled
    UIView.animate(withDuration: withDuration, delay: 0.0, options: .curveEaseInOut, animations: {
        self.alpha = self.isUserInteractionEnabled ? 1.0 : 0.0
    })
}

Alors tu appelles

yourView.animateFadeInOut()

Pourquoi self.isUserInteractionEnabled?

J'ai essayé de remplacer self.isUserInteractionEnabledpar self.isHidden, pas de chance du tout.

C'est tout. Ça me coûte du temps, j'espère que ça aide quelqu'un.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.