UIView Nascondi / Mostra con animazione


154

Il mio semplice obiettivo è sbiadire le funzioni di nascondimento e visualizzazione animate.

Button.hidden = YES;

Abbastanza semplice. Tuttavia, è possibile farlo svanire piuttosto che scomparire? Sembra piuttosto poco professionale in quel modo.

Risposte:


259

In iOS 4 e versioni successive, c'è un modo per farlo semplicemente usando il metodo di transizione UIView senza dover importare QuartzCore. Puoi solo dire:

Obiettivo C

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

veloce

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

Soluzione precedente

La soluzione di Michail funzionerà, ma in realtà non è l'approccio migliore.

Il problema con l'alfa sbiadimento è che a volte i diversi livelli di vista sovrapposti sembrano strani mentre svaniscono. Ci sono altre alternative usando Core Animation. Per prima cosa includi il framework QuartzCore nella tua app e aggiungilo #import <QuartzCore/QuartzCore.h>alla tua intestazione. Ora puoi effettuare una delle seguenti operazioni:

1) impostare button.layer.shouldRasterize = YES;e quindi utilizzare il codice di animazione alfa che Michail ha fornito nella sua risposta. Ciò eviterà che i livelli si fondano in modo strano, ma ha una leggera penalità prestazionale e può rendere sfocato il pulsante se non è allineato esattamente su un limite di pixel.

In alternativa:

2) Utilizzare invece il codice seguente per animare la dissolvenza:

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

button.hidden = YES;

La cosa bella di questo approccio è che puoi dissolvenza incrociata di qualsiasi proprietà del pulsante anche se non sono animabili (ad esempio il testo o l'immagine del pulsante), imposta la transizione e imposta le proprietà immediatamente dopo.


5
@robmathers, ho appena testato il tuo codice, sopra due codici funzionano solo quando button.hidden = NO, per dissolvenza in situazione; non ha alcun effetto animazione per la dissolvenza quando button.hidden = YES;
Jason,

Sembra essere rotto in iOS 12.0
user3532505

5
Dovresti usare la superview della cosa che stai animando come transitionWithViewparametro per garantire una dissolvenza in entrata e in uscita.
Allenh,

159

Le proprietà animate di UIView sono:

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

Descrivi in: Animazioni

isHidden non è uno di questi, quindi a mio modo di vedere il modo migliore è:

Swift 4:

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

Obiettivo C:

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

8
In realtà questa è la risposta semplice e migliore
Irshad Mohamed,

3
Mentre questo si anima correttamente, l'UISearchBar che sto provando a visualizzare appare nella posizione sbagliata fino al completamento dell'animazione e poi salta all'istante nella posizione corretta. Qualche idea? Sto usando Storyboard con Interface Builder e Vincoli.
Greg Hilston,

5
Questo codice non funziona ... cambia direttamente lo stato senza animare
Mihir Mehta,

2
@evya Funziona solo per la dissolvenza quando nascosto = NO , Non funziona per la dissolvenza, nascosto = SÌ
Jason

Eccezionale. 10 volte
Vyacheslav,

125

Per svanire:

Objective-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
}

Per sfumare:

Objective-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'uso della dissolvenza in
entrata

Per qualche motivo, l'animazione in hidden = YES ha funzionato bene per me, ma l'animazione in hidden = NO non ha fatto nulla, quindi questa combinazione di animazione dell'alfa e impostazione della proprietà nascosta è stata utile.
arlomedia,

Scrivo solo una demo, ma solo nascosto = NO, dissolvenza nelle opere, strano
Jason

9

Uso questa piccola estensione 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

rapido 4.2

con estensione:

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

metodo semplice:

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

Come posso aggiungere un ritardo per questo?
cvdogan,

7

Usa questa soluzione per ottenere effetti di dissolvenza uniforme e dissolvenza

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'uso è come

uielement.fadeIn()
uielement.fadeOut()

Grazie


fadeOutfunziona su iOS 13 solo se rimuovo le linee impostate self.isHidden.
Mike Taverne,

6

Ho creato categoria per UIViewquesto scopo e implementato un po 'po' speciale concetto diverso: visibility. La differenza principale della mia soluzione è che puoi chiamare [view setVisible:NO animated:YES]e subito dopo verificare in modo sincrono[view visible] e ottenere il risultato corretto. Questo è piuttosto semplice ma estremamente utile.

Inoltre, è consentito evitare l'uso della "logica booleana negativa" ( per ulteriori informazioni , consultare Codice completo, pagina 269, Usa nomi di variabili booleane positive ).

veloce

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
    }

}

Objective-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 versione Swift sta arrivando?
Juan Boero,

Sicuro! Ho aggiunto la versione Swift.
Valentin Shergin,

5

il codice di @Umair Afzal funziona bene in rapido 5 dopo alcune modifiche

 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)
    }
  }
}

per uso

yourView.fadeOut()
yourView.fadeIn()

dando un certo effetto durante la dissolvenza, ha aggiunto qui
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)
    }
}
}

E per usarlo, chiama semplicemente queste funzioni come:

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

Hai appena copiato la risposta di @ MarkMckelvie
Ashley Mills,

C'è una differenza, non stava nascondendo la vista. E avevo anche bisogno di nascondere la vista. Così ha fatto e condividerlo.
Umair Afzal,

3
Perché non commentare semplicemente l'altra risposta invece di copiarla e passare come tua?
Ashley Mills,

2

isHidden è un valore immediato e non è possibile influire su un'animazione al suo interno, invece di utilizzare Alpha per nascondere la vista

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

E per mostrare:

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

1

Puoi farlo MOLTO facilmente usando la libreria di 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

Puoi provare questo.

 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)
    })
}

E passa il nome della tua vista

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

1

Se la tua vista è impostata su nascosta per impostazione predefinita o cambi lo stato nascosto che penso che dovresti in molti casi, nessuno degli approcci in questa pagina ti darà l'animazione FadeIn / FadeOut, animerà solo uno di questi stati, il motivo è che stai impostando lo stato nascosto su false prima di chiamare UIView.animate metodo che causerà un'improvvisa visibilità e se solo l'alfa, lo spazio oggetti è ancora lì ma non è visibile, il che causerà alcuni problemi dell'interfaccia utente.

Quindi l'approccio migliore è verificare prima se la vista è nascosta, quindi impostare l'alfa su 0,0, in questo modo quando si imposta lo stato Nascosto su falso non si vedrà un'improvvisa visibilità.

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)
    })
}

Questo risolve il problema che altri hanno chiesto su dove i fadein non funzionavano. Approccio intelligente.
BooTooMany

1

UIView.transition (con la funzione :) è bello e pulito.

Molti l'hanno pubblicato, ma nessuno ha notato che c'è un errore che verrà visualizzato solo quando lo esegui.

È possibile passare perfettamente alla proprietà nascosta su true, mentre quando si tenta di spostarla su false, la vista scompare improvvisamente senza alcuna animazione.

Questo perché questa API funziona solo all'interno di una vista, il che significa che quando si passa una vista per mostrarla, in effetti essa stessa mostra immediatamente, solo il suo contenuto viene animato gradualmente.

Quando si tenta di nascondere questa vista, essa stessa si nasconde immediatamente, rende l'animazione al suo contenuto insignificante.

Per risolvere questo problema, quando si nasconde una vista, l'obiettivo di transizione dovrebbe essere la vista principale anziché la vista che si desidera nascondere.

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

La mia soluzione per Swift 3 . Quindi, ho creato la funzione che nasconde / mostra la vista nel giusto ordine (quando si nasconde - imposta alpha su 0 e poi è Nascosto su true; scoprendo - prima rivela la vista e poi imposta alpha su 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)
}

Perché nel completionblocco c'è un'altra animazione quando hideè falso?
Giorgio,

0

Swift 4 Transition

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

Se usi l'approccio per le versioni precedenti di Swift otterrai un errore:

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

Riferimento utile .


funziona con autolayout? codice simile non sta animando. il isHiddenvalore viene reso istantaneamente (cioè nascondendo / mostrando istantaneamente la vista).
Crashalot,

0

Questo codice fornisce un'animazione come spingere viewController nel controller di navigazione aerea ...

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

 _viewAccountName.hidden = true;

Usato per l'animazione 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

Ho provato alcune delle risposte uscite, alcune funzionano solo per una situazione, alcune hanno bisogno di aggiungere due funzioni.

opzione 1

Niente a che fare con 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
        })
    }
}

Quindi passa isFadeIn( trueo false)

view.animate(fadeIn: isFadeIn) 

opzione 2

Non passare alcun parametro. Sfuma dentro o fuori secondo isUserInteractionEnabled. Questo si adatta anche alla situazione animata avanti e indietro molto bene.

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
    })
}

Quindi chiami

yourView.animateFadeInOut()

Perché self.isUserInteractionEnabled?

Provato a sostituire self.isUserInteractionEnabledcon self.isHidden, senza fortuna a tutti.

Questo è tutto. Mi costa qualche volta, spero che aiuti qualcuno.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.