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.

JTApps
la source

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.

Nick Lockwood
la source
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];
}
Evya
la source
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
}
Mikhail Grebionkin
la source
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)
  }
}
Mark Mckelvie
la source
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
     })
}
Scaraux
la source
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
    })
}
Mohsen mokhtari
la source
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

Dhanu K
la source
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
Valentin Shergin
la source
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()
Sanjay Mishra
la source
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
Umair Afzal
la source
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
})
Mohsen
la source
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
Nikita Arkhipov
la source
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

    })


}
Vimal Saifudin
la source
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)
Kalpesh
la source
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)
    })
}
Shahriar
la source
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)
}
LiLi Kazine
la source
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)
}
Nazariy Vlizlo
la source
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 .

nanospeck
la source
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;
Prasanna
la source
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.

William Hu
la source