Come applicare la dissolvenza a un UIVisualEffectView e / o UIBlurEffect?


92

Voglio sfumare un UIVisualEffectsView con un UIBlurEffect dentro e fuori:

var blurEffectView = UIVisualEffectView()
blurEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))

Uso un'animazione normale all'interno di una funzione chiamata da a UIButtonper sfumarla in apertura, lo stesso per la dissolvenza in uscita ma .alpha = 0& hidden = true:

blurEffectView.hidden = false
UIView.animate(withDuration: 1, delay: 0, options: .curveEaseOut) {
    self.blurEffectView.alpha = 1
}

Ora, dissolvenza in entrambe le direzioni funziona ma mi dà un errore quando dissolvenza out :

<UIVisualEffectView 0x7fdf5bcb6e80>viene chiesto di animare la sua opacità. In questo modo l'effetto apparirà interrotto fino a quando l'opacità non torna a 1.

Domanda

Come faccio a sfumare con successo l'entrata UIVisualEffectViewe l'uscita senza romperlo e avere una transizione in dissolvenza?

Nota

  • Ho provato a metterlo UIVisualEffectViewin a UIViewe a sfumarlo, senza successo

Puoi evitare l'errore: 1. NON assegnando uno stile iniziale a blurEffect. cioè, var blurEffectView = UIVisualEffectView () e poi 2. assegnare blurEffect a blurEffectView ALL'INTERNO del blocco di animazione. 3. l'animazione "l'assegnazione di blurEffect a blurEffectView" viene eseguita al posto delle regolazioni a blurEffectView.alpha. ** L'errore apparirà anche se assegni un valore iniziale, lo nulli e poi lo aggiungi di nuovo. Quindi la chiave per prevenire l'errore è non assegnare l'effetto a blurEffectView fino al blocco dell'animazione.
ObiettivoTC

Inoltre, è necessario menzionare che blurEffectView deve avere alpha = 1.0 per evitare l'errore. La manomissione di alpha in qualsiasi modo produrrà l'errore. Ad esempio, prima del blocco dell'animazione si imposta blurEffectView.alpha = 0.7, quindi in animationBlock si assegna blurEffectView.effect = blurEffect, viene prodotto l'errore.
ObjectiveTC

Risposte:


185

Penso che questo sia nuovo in iOS9, ma ora puoi impostare l'effetto di un UIVisualEffectViewblocco di animazione all'interno di:

let overlay = UIVisualEffectView()
// Put it somewhere, give it a frame...
UIView.animate(withDuration: 0.5) {
    overlay.effect = UIBlurEffect(style: .light)
}

Impostalo su nilper rimuovere.

MOLTO IMPORTANTE: quando si esegue il test sul simulatore, assicurarsi di impostare l'override della qualità grafica del simulatore su Alta qualità affinché funzioni.


1
Funziona perfettamente. Grazie.
Baza207

Grande! Come hai detto, non funziona per iOS 8 però: /
LinusGeffarth

Ebbene, proprio per quello che ho scritto sopra. L'ho comunque approvato adesso. @jpros
LinusGeffarth

10
Puoi anche impostare l'effetto a zero per ottenere un bel "sfocato"
jpros

1
@jpros Ti dispiacerebbe spiegare cosa intendi per "sfocatura?"
ndmeiri

19

La documentazione di Apple (attualmente) afferma ...

Quando si utilizza la classe UIVisualEffectView, evitare valori alfa inferiori a 1.

e

L'impostazione dell'alfa su un valore inferiore a 1 nella vista degli effetti visivi o in una qualsiasi delle sue superview fa sì che molti effetti sembrino errati o non vengano visualizzati affatto.

Credo che qui manchi un contesto importante ...

Suggerirei che l'intento è quello di evitare valori alfa inferiori a 1 per una visualizzazione persistente. A mio modesto parere questo non si applica all'animazione di una vista.

Il mio punto: suggerirei che i valori alfa inferiori a 1 siano accettabili per le animazioni.

Il messaggio del terminale afferma:

A UIVisualEffectView viene chiesto di animare la sua opacità. In questo modo l'effetto apparirà interrotto fino a quando l'opacità non torna a 1.

Leggendo con attenzione, sarà l'effetto appare essere rotto. I miei punti su questo essere:

  • l'apparente rottura conta davvero solo per una visione che è persistente - non cambia;
  • una visualizzazione persistente / immutabile UIVisualEffectcon un valore alfa inferiore a 1 non si presenterà come previsto / progettato da Apple; e
  • il messaggio nel terminale non è un errore, solo un avvertimento.

Per estendere la risposta di @ jrturton sopra che mi ha aiutato a risolvere il mio problema, aggiungerei ...

Per sfumare, UIVisualEffectutilizzare il seguente codice (Objective-C):

UIView.animateWithDuration(1.0, animations: {
//  EITHER...
    self.blurEffectView.effect = UIBlurEffect(nil)
//  OR...
    self.blurEffectView.alpha = 0
}, completion: { (finished: Bool) -> Void in
    self.blurEffectView.removeFromSuperview()
} )

Uso con successo entrambi i metodi: impostando la effectproprietà su nile impostando la alphaproprietà su 0.

Notare che l'impostazione di effecta nilcrea un "bel flash" (in mancanza di una descrizione migliore) alla fine dell'animazione, mentre l'impostazione di alphaa 0crea una transizione graduale.

(Fammi sapere eventuali errori di sintassi ... scrivo in obj-c.)


Grazie per il tuo contributo. Ho capito il tuo punto; l'errore è stato risolto per me già con esattamente quello che hai menzionato :)
LinusGeffarth

14

Ecco la soluzione che ho trovato che funziona sia su iOS10 che su versioni precedenti utilizzando Swift 3

extension UIVisualEffectView {

    func fadeInEffect(_ style:UIBlurEffectStyle = .light, withDuration duration: TimeInterval = 1.0) {
        if #available(iOS 10.0, *) {
            let animator = UIViewPropertyAnimator(duration: duration, curve: .easeIn) {
                self.effect = UIBlurEffect(style: style)
            }

            animator.startAnimation()
        }else {
            // Fallback on earlier versions
            UIView.animate(withDuration: duration) {
                self.effect = UIBlurEffect(style: style)
            }
        }
    }

    func fadeOutEffect(withDuration duration: TimeInterval = 1.0) {
        if #available(iOS 10.0, *) {
            let animator = UIViewPropertyAnimator(duration: duration, curve: .linear) {
                self.effect = nil
            }

            animator.startAnimation()
            animator.fractionComplete = 1
        }else {
            // Fallback on earlier versions
            UIView.animate(withDuration: duration) {
                self.effect = nil
            }
        }
    }

}

Puoi anche controllare questa sintesi per trovare un esempio di utilizzo.


L'utilizzo di UIViewPropertyAnimatorè l'unica cosa che sembra funzionare in iOS 11. Grazie per questo!
LinusGeffarth,

Non dimenticare di aggiungere: importa UIKit
Oleksandr

8

Solo una soluzione alternativa: inserire UIVisualEffectViewuna visualizzazione contenitore e modificare la alphaproprietà per quel contenitore. Questo approccio funziona perfettamente per me su iOS 9. Sembra che non funzioni più in iOS 10.


Freddo. Grazie, ci proverò :)
LinusGeffarth

1
Puoi fornire un codice di esempio. Questo non sembra funzionare.
cnotethegr8

Funziona per me in iOS 9
ore,

@DerrickHunt Lo stesso qui. Hai trovato una soluzione?
damirstuhec

2
@damirstuhec Se il tuo progetto è solo iOS 10, puoi utilizzare la nuova classe UIViewPropertyAnimator per animare la forza di UIBlurView. Se è necessario supportare versioni precedenti, è possibile utilizzare il codice sopra e la parola chiave #available per utilizzare UIViewPropertyAnimator per i dispositivi che eseguono 10. Spero che questo aiuti!
Derrick Hunt,

5

Puoi cambiare l'alfa della visualizzazione degli effetti visivi senza problemi, a parte l'avviso nella console. La vista può apparire semplicemente parzialmente trasparente, anziché sfocata. Ma questo di solito non è un problema se stai solo cambiando l'alfa durante l'animazione.

La tua app non andrà in crash o verrà rifiutata per questo. Provalo su un dispositivo reale (o otto). Se sei soddisfatto di come appare e si comporta, va bene. Apple ti sta solo avvertendo che potrebbe non avere un aspetto o prestazioni altrettanto buone di una visualizzazione degli effetti visivi con un valore alfa di 1.


5

È possibile acquisire un'istantanea di una vista sottostante statica e applicare una dissolvenza in entrata e in uscita senza toccare l'opacità della vista sfocata. Supponendo un ivar di blurView:

func addBlur() {
    guard let blurEffectView = blurEffectView else { return }

    //snapShot = UIScreen.mainScreen().snapshotViewAfterScreenUpdates(false)
    let snapShot = self.view.snapshotViewAfterScreenUpdates(false)
    view.addSubview(blurEffectView)
    view.addSubview(snapShot)

    UIView.animateWithDuration(0.25, animations: {
        snapShot.alpha = 0.0
    }, completion: { (finished: Bool) -> Void in
        snapShot.removeFromSuperview()
    } )
}

func removeBlur() {
    guard let blurEffectView = blurEffectView else { return }

    let snapShot = self.view.snapshotViewAfterScreenUpdates(false)
    snapShot.alpha = 0.0
    view.addSubview(snapShot)

    UIView.animateWithDuration(0.25, animations: {
        snapShot.alpha = 1.0
    }, completion: { (finished: Bool) -> Void in
        blurEffectView.removeFromSuperview()
        snapShot.removeFromSuperview()
    } )
}

Bella idea, ma non funzionerà bene per contenuti non statici (in movimento / animazione) sotto la visualizzazione sfocata.
LinusGeffarth

@LinusG. Bene, Apple dice che non dovrebbe giocare con una vista sfocata alfa. È possibile utilizzare CADisplayLink per acquisire schermate della vista di base, quindi renderizzarle in una vista immagine (forse una che hai fatto tu - molto leggera) - e mostrarle nella vista dall'alto mentre cambia la sua opacità a 0. Possibile ma molte lavoro.
David H

1
Questo è stato l'unico modo (finora) in cui sono stato in grado di ottenere ciò che volevo in iOS 10: dissolvenza in modalità modale su schermo intero, con un effetto sfocato molto scuro. Grazie!
Graham

3

Se vuoi sfumare in te UIVisualEffectView - per ios10 usa UIViewPropertyAnimator

    UIVisualEffectView *blurEffectView = [[UIVisualEffectView alloc] initWithEffect:nil];
    blurEffectView.frame = self.view.frame;
    blurEffectView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

    UIView *blackUIView = [[UIView alloc]initWithFrame:self.view.frame];
    [bacgroundImageView addSubview:blackUIView];
    [blackUIView addSubview:blurEffectView];
    UIViewPropertyAnimator *animator  = [[UIViewPropertyAnimator alloc] initWithDuration:4.f curve:UIViewAnimationCurveLinear animations:^{
        [blurEffectView setEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]];
    }];

quindi puoi impostare la percentuale

[animator setFractionComplete:percent];

per ios9 puoi usare il componente alpha


2
La domanda è specificatamente contrassegnata come "rapida", non "obiettivo-c": fornisci una risposta in Swift. Grazie!
Eric Aya

1
Grazie per il codice dell'obiettivo C. L'ho trovato utile perché non c'erano molti altri esempi elencati.
Adam Ware

2

L'alfa di UIVisualEffectView deve sempre essere 1. Penso che tu possa ottenere l'effetto impostando l'alfa del colore di sfondo.

Fonte: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIVisualEffectView/index.html


Peter, puoi spiegare come impostare l'alfa del colore di sfondo?
Boris Y.

@borisy usa il colorWithAlphaComponent:metodo sulle UIColoristanze. Non sono sicuro di come ottenere lo stesso effetto impostando il colore di sfondo, però.
nemesi

Modificando il componente alfa di sfondo non funziona come previsto
Honghao Zhang

1

Faccio una uiview quale alpha è 0 e aggiungo blurview come subview di quella. Quindi posso nascondere / mostrare o arrotondare gli angoli con l'animazione.


1

Ho finito con la seguente soluzione, utilizzando animazioni separate per UIVisualEffectViewe il contenuto. Ho usato il viewWithTag()metodo per ottenere un riferimento aUIView all'interno del file UIVisualEffectView.

let blurEffectView = UIVisualEffectView()
// Fade in
UIView.animateWithDuration(1) { self.blurEffectView.effect = UIBlurEffect(style: .Light) }    
UIView.animateWithDuration(1) { self.blurEffectView.viewWithTag(1)?.alpha = 1 }
// Fade out
UIView.animateWithDuration(1) { self.blurEffectView.effect = nil }    
UIView.animateWithDuration(1) { self.blurEffectView.viewWithTag(1)?.alpha = 0 }

Preferirei la singola animazione cambiando l'alfa, ma questo evita l'errore e sembra funzionare altrettanto bene.


1

Ho appena avuto questo problema e il modo in cui l'ho aggirato è stato quello di ospitare UIVisualEffectsView in un UIView e animare l'alfa di UIView.

Ha funzionato bene, tranne per il fatto che non appena l'alfa è cambiato al di sotto di 1.0 è diventato un bianco solido e sembrava molto stridente. Per aggirare questo problema, è necessario impostare la proprietà layer di UIViewcontainerView.layer.allowsGroupOpacity = false e questo impedirà che lampeggi in bianco.

Ora puoi animare in entrata / in uscita la UIView contenente la vista degli effetti visivi e qualsiasi altra sottoview utilizzando la sua proprietà alpha e non devi preoccuparti di alcun problema grafico o di registrare un messaggio di avviso.


0
_visualEffectView.contentView.alpha = 0;

Per cambiare l'alfa di UIVisualEffectView, dovresti cambiare la contentView di _visualEffectView. Se cambi l'alfa di _visualEffectView, otterrai questo

<UIVisualEffectView 0x7ff7bb54b490> is being asked to animate its opacity. This will cause the effect to appear broken until opacity returns to 1.

0

Di solito, voglio solo animare una sfocatura quando presento un controller di visualizzazione sullo schermo e desidero sfocare il controller di visualizzazione di presentazione. Ecco un'estensione che aggiunge blur () e unblur () a un controller di visualizzazione per facilitare ciò:

extension UIViewController {

   func blur() {
       //   Blur out the current view
       let blurView = UIVisualEffectView(frame: self.view.frame)
       self.view.addSubview(blurView)
       UIView.animate(withDuration:0.25) {
           blurView.effect = UIBlurEffect(style: .light)
       }
   }

   func unblur() {
       for childView in view.subviews {
           guard let effectView = childView as? UIVisualEffectView else { continue }
           UIView.animate(withDuration: 0.25, animations: {
                effectView.effect = nil
           }) {
               didFinish in
               effectView.removeFromSuperview()
           }
       }
   }
}

Ovviamente puoi renderlo più robusto consentendo all'utente di scegliere lo stile dell'effetto, modificare la durata, chiamare qualcosa quando l'animazione è completata, taggare la vista dell'effetto visivo aggiunto in blur () per assicurarti che sia l'unica rimossa quando annulli la sfocatura ( ), ecc., ma finora non ho trovato la necessità di fare queste cose, poiché questo tende ad essere un tipo di operazione "spara e dimentica".


"spara e dimentica" è così riconoscibile! Ha
LinusGeffarth

0

in base alla risposta di @ cc ho modificato la sua estensione per sfocare una vista

estensione UIView {

    func blur () {
        // Sfoca la visualizzazione corrente
        let blurView = UIVisualEffectView (frame: self.bounds)
        self.addSubview (blurView)
        UIView.animate (withDuration: 0.25) {
            blurView.effect = UIBlurEffect (stile: .dark)
        }
    }

    func unblur () {
        for childView in subviews {
            guardia lascia effectView = childView come? UIVisualEffectView altro {continue}
            UIView.animate (withDuration: 2.5, animazioni: {
                effectView.effect = nil
            }) {
                didFinish in
                effectView.removeFromSuperview ()
            }
        }
    }
}

0

Migliorando la risposta @ Tel4tel e @cc, ecco un'estensione con parametri e una breve spiegazione.

extension UIView {

    // Perform a blur animation in the whole view
    // Effect tone can be .light, .dark, .regular...
    func blur(duration inSeconds: Double, effect tone: UIBlurEffectStyle) {
        let blurView = UIVisualEffectView(frame: self.bounds)
        self.addSubview(blurView)
        UIView.animate(withDuration: inSeconds) {
            blurView.effect = UIBlurEffect(style: tone)
        }
    }

    // Perform an unblur animation in the whole view
    func unblur(duration inSeconds: Double) {
        for childView in subviews {
            guard let effectView = childView as? UIVisualEffectView else { continue 
        }
            UIView.animate(withDuration: inSeconds, animations: {
                effectView.effect = nil
            }){
                didFinish in effectView.removeFromSuperview()
            }
        }
    }
}

Quindi puoi usarlo come: view.blur(duration: 5.0, effect: .light) o view.unblur(duration: 3.0)

Ricorda di NON usarlo in viewDidLoad()quanto sovrascriverà l'animazione. Inoltre, quando si esegue su un simulatore, impostare la grafica al livello superiore per poter vedere l'animazione (Debug> Sostituzione qualità grafica> Alta qualità).

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.