¿Cómo fundir un UIVisualEffectView y / o UIBlurEffect dentro y fuera?

92

Quiero desvanecer un UIVisualEffectsView con un UIBlurEffect dentro y fuera:

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

Utilizo una animación normal dentro de una función llamada por a UIButtonpara fundirla, lo mismo para desaparecer pero .alpha = 0& hidden = true:

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

Ahora, la decoloración en ambas direcciones funciona pero me da un error cuando la decoloración a cabo :

<UIVisualEffectView 0x7fdf5bcb6e80>se le pide que anime su opacidad. Esto hará que el efecto parezca roto hasta que la opacidad vuelva a 1.

Pregunta

¿Cómo puedo desvanecer la entrada UIVisualEffectViewy la salida sin romperla y tener una transición de desvanecimiento?

Nota

  • Traté de poner el UIVisualEffectViewen UIVieway desvanecer ese, sin éxito
LinusGeffarth
fuente
Puede evitar el error: 1. NO asignando un estilo inicial al efecto de desenfoque. es decir, var blurEffectView = UIVisualEffectView () y luego 2. asigna el blurEffect al blurEffectView DENTRO del bloque de animación. 3. La animación de la "asignación de blurEffect a blurEffectView" se realiza en lugar de ajustes a blurEffectView.alpha. ** El error aparecerá incluso si asigna un valor inicial, nulo y luego lo vuelve a agregar. Entonces, la clave para prevenir el error es no asignar el efecto a blurEffectView hasta el bloque de animación.
ObjectiveTC
También es necesario mencionar que blurEffectView debe tener alpha = 1.0 para evitar el error. La manipulación de alpha de cualquier forma producirá el error. Por ejemplo, antes del bloque de animación configuras blurEffectView.alpha = 0.7, luego en animationBlock asignas blurEffectView.effect = blurEffect, se produce el error.
ObjectiveTC

Respuestas:

186

Creo que esto es nuevo en iOS9, pero ahora puedes establecer el efecto de un UIVisualEffectViewbloque de animación dentro de un:

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

Configúrelo en nilpara eliminar.

MUY IMPORTANTE: cuando pruebe esto en el simulador, asegúrese de configurar la Anulación de calidad de gráficos de su simulador en Alta calidad para que esto funcione.

jrturton
fuente
1
Funciona perfectamente. Gracias.
Baza207
¡Excelente! Sin embargo, como dijiste, no funciona para iOS 8: /
LinusGeffarth
Bueno, exactamente por lo que escribí arriba. Lo aprobé ahora de todos modos. @jpros
LinusGeffarth
10
También puede establecer el efecto en nulo para obtener un "desenfoque"
bonito
1
@jpros ¿Le importaría explicar lo que quiere decir con "difuminar"?
ndmeiri
19

La documentación de Apple (actualmente) dice ...

Cuando use la clase UIVisualEffectView, evite valores alfa que sean menores que 1.

y

Establecer el alfa en menos de 1 en la vista de efectos visuales o cualquiera de sus supervistas hace que muchos efectos se vean incorrectos o no se muestren en absoluto.

Creo que aquí falta un contexto importante ...

Sugeriría que la intención es evitar valores alfa inferiores a 1 para una vista persistente. En mi humilde opinión, esto no se aplica a la animación de una vista.

Mi punto: sugeriría que los valores alfa inferiores a 1 son aceptables para las animaciones.

El mensaje del terminal dice:

Se solicita a UIVisualEffectView que anime su opacidad. Esto hará que el efecto parezca roto hasta que la opacidad vuelva a 1.

Al leer esto con atención, el efecto parecerá estar roto. Mis puntos sobre este ser:

  • la ruptura aparente sólo importa realmente para una visión que es persistente, no cambia;
  • una vista persistente / invariable UIVisualEffectcon un valor alfa inferior a 1 no se presentará según lo previsto / diseñado por Apple; y
  • el mensaje en la terminal no es un error, solo una advertencia.

Para extender la respuesta de @jrturton anterior que me ayudó a resolver mi problema, agregaría ...

Para atenuar el UIVisualEffectuso del siguiente código (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()
} )

Utilizo con éxito ambos métodos: estableciendo la effectpropiedad en nily estableciendo la alphapropiedad en 0.

Tenga en cuenta que al configurar el effectpara nilcrear un "flash agradable" (a falta de una mejor descripción) al final de la animación, mientras que el establecer el alphapara 0crea una transición suave.

(Avíseme cualquier error de sintaxis ... escribo en obj-c.)

andrewbuilder
fuente
Gracias por tu aporte. Entiendo tu punto; el error ya está resuelto para mí con exactamente lo que mencionaste :)
LinusGeffarth
14

Aquí está la solución que terminé y que funciona tanto en iOS10 como en versiones anteriores con 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
            }
        }
    }

}

También puede consultar esta esencia para encontrar un uso de ejemplo.

Gunhan
fuente
Usar el UIViewPropertyAnimatores lo único que parece funcionar en iOS 11. ¡Gracias por esto!
LinusGeffarth
No olvide agregar: importar UIKit
Oleksandr
8

Solo una solución: colóquelo UIVisualEffectViewen una vista de contenedor y cambie la alphapropiedad de ese contenedor. Ese enfoque me funciona perfectamente en iOS 9. Parece que ya no funciona en iOS 10.

berilio
fuente
Frio. Gracias, lo intentaré :)
LinusGeffarth
1
¿Puede proporcionar un código de muestra? Esto no parece funcionar.
cnotethegr8
Funciona para mí en iOS 9
hr
@DerrickHunt Lo mismo aquí. ¿Encontraste una solución?
damirstuhec
2
@damirstuhec Si su proyecto es solo iOS 10, puede usar la nueva clase UIViewPropertyAnimator para animar la fuerza de UIBlurView. Si necesita admitir versiones anteriores, puede usar el código anterior y la palabra clave #available para usar UIViewPropertyAnimator para dispositivos que ejecutan 10. ¡Espero que esto ayude!
Derrick Hunt
5

Puede cambiar el alfa de la vista de efectos visuales sin ningún problema, aparte de la advertencia en la consola. La vista puede parecer simplemente parcialmente transparente, en lugar de borrosa. Pero esto generalmente no es un problema si solo está cambiando el alfa durante la animación.

Su aplicación no se bloqueará ni será rechazada por esto. Pruébelo en un dispositivo real (u ocho). Si está satisfecho con su apariencia y rendimiento, está bien. Apple solo le advierte que es posible que no se vea o no funcione tan bien como una vista de efectos visuales con un valor alfa de 1.

Dave Batton
fuente
5

Puede tomar una instantánea de una vista subyacente estática y hacerla aparecer y desaparecer sin tocar la opacidad de la vista borrosa. Suponiendo un ivar de 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()
    } )
}
David H
fuente
También es una buena idea, pero no funcionará bien para contenido no estático (en movimiento / animado) debajo de la vista borrosa.
LinusGeffarth
@LinusG. Bueno, Apple dice que no debería jugar con una vista borrosa alfa. Puede usar CADisplayLink para tomar capturas de pantalla de su vista base, luego renderizarlas en una vista de imagen (quizás una que hizo, realmente liviana) - y mostrarlas en la vista superior mientras cambia su opacidad a 0. Posible, pero mucho trabajo.
David H
1
Esta fue la única forma (hasta ahora) que he podido lograr lo que quería en iOS 10: desvanecer en un modo sobre pantalla completa, con un efecto de desenfoque muy oscuro. ¡Gracias!
Graham
3

Si desea desvanecerse en su UIVisualEffectView - para ios10 use 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]];
    }];

entonces puedes establecer el porcentaje

[animator setFractionComplete:percent];

para ios9 puedes usar el componente alfa

kiril kiroski
fuente
2
La pregunta está etiquetada específicamente como "rápido", no "objetivo-c": proporcione una respuesta en Swift. ¡Gracias!
Eric Aya
1
Gracias por el código Objective C. Lo encontré útil ya que no había muchos otros ejemplos enumerados.
Adam Ware
2

El alfa de UIVisualEffectView siempre tiene que ser 1. Creo que puede lograr el efecto configurando el alfa del color de fondo.

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

Peter Lendvay
fuente
Peter, ¿podrías explicar cómo establecer el alfa del color de fondo?
Boris Y.14 de
@borisy usa el colorWithAlphaComponent:método en las UIColorinstancias. Sin embargo, no estoy seguro de cómo lograr el mismo efecto con la configuración del color de fondo.
némesis
Al cambiar el componente alfa de fondo no funciona como se esperaba
Honghao Zhang
1

Hago una vista de usuario que alfa es 0 y agrego vista borrosa como subvista de eso. Entonces puedo ocultar / mostrar o redondear las esquinas con animación.

Kemal Can Kaynak
fuente
1

Terminé con la siguiente solución, usando animaciones separadas para UIVisualEffectViewy el contenido. Usé el viewWithTag()método para obtener una referencia al UIViewinterior del 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 }

Preferiría que la animación única cambiara el alfa, pero esto evita el error y parece funcionar igual de bien.

demian
fuente
1

Acabo de tener este problema y la forma en que lo resolví fue albergar UIVisualEffectsView en una UIView y animar el alfa de esa UIView.

Esto funcionó bien, excepto que tan pronto como el alfa cambió por debajo de 1.0 se convirtió en un blanco sólido y se veía muy discordante. Para evitar esto, debe establecer la propiedad de capa de UIView containerView.layer.allowsGroupOpacity = falsey esto evitará que parpadee en blanco.

Ahora puede animar dentro / fuera de la UIView que contiene la vista de efectos visuales y cualquier otra subvista usando su propiedad alfa y no tener que preocuparse por fallas gráficas o por registrar un mensaje de advertencia.

usuario1898712
fuente
0
_visualEffectView.contentView.alpha = 0;

Para cambiar el alfa de UIVisualEffectView, debe cambiar el contentView de _visualEffectView. Si cambia el alfa de _visualEffectView, obtendrá esto

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

Por lo general, solo quiero animar un desenfoque cuando estoy presentando un controlador de vista sobre la pantalla y quiero desenfocar el controlador de vista de presentación. Aquí hay una extensión que agrega desenfoque () y unblur () a un controlador de vista para facilitar eso:

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

Por supuesto, puede hacer esto más robusto permitiendo que el usuario elija el estilo del efecto, modifique la duración, llame a algo cuando se complete la animación, etiquete la vista de efecto visual agregado en desenfoque () para asegurarse de que sea el único que se elimine al desenfocar () ), etc., pero no he encontrado la necesidad de hacer estas cosas hasta ahora, ya que tiende a ser un tipo de operación de "disparar y olvidar".

cc.
fuente
¡"dispara y olvida" es tan identificable! Ha
LinusGeffarth
0

basado en la respuesta de @ cc modifiqué su extensión para difuminar una vista

extensión UIView {

    func blur () {
        // Desenfocar la vista actual
        deje blurView = UIVisualEffectView (marco: self.bounds)
        self.addSubview (blurView)
        UIView.animate (withDuration: 0.25) {
            blurView.effect = UIBlurEffect (estilo: .dark)
        }
    }

    func unblur () {
        para childView en subvistas {
            guard dejar effectView = childView como? UIVisualEffectView else {continue}
            UIView.animate (withDuration: 2.5, animaciones: {
                effectView.effect = nil
            }) {
                terminó en
                effectView.removeFromSuperview ()
            }
        }
    }
}
Tel4tel
fuente
0

Mejorando la respuesta de @ Tel4tel y @cc, aquí hay una extensión con parámetros y una breve explicación.

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

Entonces puedes usarlo como: view.blur(duration: 5.0, effect: .light) o view.unblur(duration: 3.0)

Recuerde NO usarlo viewDidLoad()ya que anulará la animación. Además, cuando se ejecuta en un simulador, cambie los gráficos al nivel superior para poder ver la animación (Depurar> Anulación de calidad de gráficos> Alta calidad).

Rafael Díaz
fuente