Comment faire fondre un UIVisualEffectView et / ou UIBlurEffect en entrée et en sortie?

92

Je veux fondre un UIVisualEffectsView avec un UIBlurEffect entrant et sortant:

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

J'utilise une animation normale dans une fonction appelée par a UIButtonpour la fondre, idem pour le fondu mais .alpha = 0& hidden = true:

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

Maintenant, la décoloration dans les deux sens fonctionne , mais il me donne une erreur lors de la décoloration sur :

<UIVisualEffectView 0x7fdf5bcb6e80>est invité à animer son opacité. Cela fera apparaître l'effet interrompu jusqu'à ce que l'opacité revienne à 1.

Question

Comment réussir à fondre l'entrée UIVisualEffectViewet la sortie sans le casser et avoir une transition de fondu?

Remarque

  • J'ai essayé de mettre le UIVisualEffectViewdans un UIViewet de fondre celui-là, sans succès
LinusGeffarth
la source
Vous pouvez éviter l'erreur en: 1. N'attribuant PAS un style initial au blurEffect. c.-à-d. var blurEffectView = UIVisualEffectView (), puis 2. attribuer le blurEffect au blurEffectView À L'INTÉRIEUR du bloc d'animation. 3. l'animation de "l'affectation de blurEffect à blurEffectView" se fait à la place des ajustements de blurEffectView.alpha. ** L'erreur apparaîtra même si vous attribuez une valeur initiale, nulle, puis rajoutez-la. Donc, la clé pour éviter l'erreur est de ne pas affecter l'effet à blurEffectView avant le bloc d'animation.
ObjectiveTC
Il faut également mentionner que le blurEffectView doit avoir alpha = 1.0 pour éviter l'erreur. La falsification de l'alpha de quelque manière que ce soit produira l'erreur. Par exemple, avant le bloc d'animation, vous définissez blurEffectView.alpha = 0.7, puis dans l'animationBlock vous affectez blurEffectView.effect = blurEffect, l'erreur est produite.
ObjectiveTC

Réponses:

185

Je pense que c'est nouveau dans iOS9, mais vous pouvez maintenant définir l'effet d'un UIVisualEffectViewbloc d'animation à l'intérieur d'un:

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

Réglez-le sur nilpour supprimer.

TRÈS IMPORTANT - Lorsque vous testez cela sur le simulateur, assurez-vous de régler le remplacement de la qualité graphique de votre simulateur sur Haute qualité pour que cela fonctionne.

jrturton
la source
1
Fonctionne parfaitement. Merci.
Baza207
Génial! Comme vous l'avez dit, ne fonctionne pas pour iOS 8: /
LinusGeffarth
Eh bien exactement à cause de ce que j'ai écrit ci-dessus. Je l'ai approuvé maintenant de toute façon. @jpros
LinusGeffarth
10
Vous pouvez également définir l'effet sur zéro pour obtenir un joli "flou"
jpros
1
@jpros Pourriez-vous expliquer ce que vous entendez par «flou»?
ndmeiri
19

La documentation Apple indique (actuellement) ...

Lorsque vous utilisez la classe UIVisualEffectView, évitez les valeurs alpha inférieures à 1.

et

Si vous définissez l'alpha sur moins de 1 sur la vue des effets visuels ou sur l'une de ses super-vues, de nombreux effets semblent incorrects ou n'apparaissent pas du tout.

Je pense qu'il manque ici un contexte important ...

Je suggérerais que l'intention est d'éviter les valeurs alpha inférieures à 1 pour une vue persistante. À mon humble avis, cela ne s'applique pas à l'animation d'une vue.

Mon point - je suggère que les valeurs alpha inférieures à 1 sont acceptables pour les animations.

Le message du terminal indique:

UIVisualEffectView est invité à animer son opacité. Cela fera apparaître l'effet interrompu jusqu'à ce que l'opacité revienne à 1.

En lisant attentivement ceci, l'effet semblera rompu. Mes points à ce sujet étant:

  • la rupture apparente n'a vraiment d'importance que pour une vue qui est persistante - ne change pas;
  • une vue persistante / inchangée UIVisualEffectavec une valeur alpha inférieure à 1 ne sera pas présentée comme prévu / conçu par Apple; et
  • le message dans le terminal n'est pas une erreur, juste un avertissement.

Pour étendre la réponse de @ jrturton ci-dessus qui m'a aidé à résoudre mon problème, j'ajouterais ...

Pour disparaître, UIVisualEffectutilisez le code (Objective-C) suivant:

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

J'utilise avec succès les deux méthodes: définir la effectpropriété sur nilet définir la alphapropriété sur 0.

Notez que le réglage de effectsur nilcrée un "joli flash" (faute d'une meilleure description) à la fin de l'animation, tandis que le réglage de alphasur 0crée une transition douce.

(Faites-moi savoir toute erreur de syntaxe ... j'écris en obj-c.)

Andrewbuilder
la source
Merci pour votre contribution. Je comprends ton point de vue; l'erreur est déjà résolue pour moi avec exactement ce que vous avez mentionné :)
LinusGeffarth
14

Voici la solution que j'ai trouvée qui fonctionne à la fois sur iOS10 et les versions antérieures en utilisant 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
            }
        }
    }

}

Vous pouvez également consulter cet essentiel pour trouver un exemple d'utilisation.

Gunhan
la source
L'utilisation de UIViewPropertyAnimatorest la seule chose qui semble fonctionner sous iOS 11. Merci pour cela!
LinusGeffarth
N'oubliez pas d'ajouter: import UIKit
Oleksandr
8

Juste une solution de contournement - mettez UIVisualEffectViewdans une vue de conteneur et modifiez la alphapropriété de ce conteneur. Cette approche fonctionne parfaitement pour moi sur iOS 9. Il semble que cela ne fonctionne plus sous iOS 10.

béryllium
la source
Cool. Merci, je vais essayer :)
LinusGeffarth
1
Pouvez-vous fournir un exemple de code. Cela ne semble pas fonctionner.
cnotethegr8
Fonctionne pour moi dans iOS 9
5hrp
@DerrickHunt Même chose ici. Avez-vous trouvé une solution?
damirstuhec le
2
@damirstuhec Si votre projet est iOS 10 uniquement, vous pouvez utiliser la nouvelle classe UIViewPropertyAnimator pour animer la force UIBlurView. Si vous avez besoin de prendre en charge des versions plus anciennes, vous pouvez utiliser le code ci-dessus et utiliser le mot clé #available pour utiliser UIViewPropertyAnimator pour les appareils exécutant 10. J'espère que cela vous aidera!
Derrick Hunt
5

Vous pouvez modifier l'alpha de la vue des effets visuels sans aucun problème, autre que l'avertissement dans la console. La vue peut apparaître comme simplement partiellement transparente, plutôt que floue. Mais ce n'est généralement pas un problème si vous ne faites que changer l'alpha pendant l'animation.

Votre application ne va pas planter ou être rejetée pour cela. Testez-le sur un vrai appareil (ou huit). Si vous êtes satisfait de son apparence et de ses performances, tout va bien. Apple vous avertit simplement qu'il peut ne pas avoir l'air ou fonctionner aussi bien qu'une vue d'effets visuels avec une valeur alpha de 1.

Dave Batton
la source
5

Vous pouvez prendre un instantané d'une vue sous-jacente statique et l'afficher en fondu sans toucher à l'opacité de la vue floue. En supposant un ivar de flouView:

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
la source
Bonne idée également, mais cela ne fonctionnera pas bien pour le contenu non statique (en mouvement / animation) sous la vue floue.
LinusGeffarth
@LinusG. Eh bien, Apple dit qu'il ne faut pas jouer avec une vue floue alpha. Vous pouvez utiliser CADisplayLink pour capturer des captures d'écran de votre vue de base, puis les rendre dans une vue d'image (peut-être celle que vous avez faite - vraiment légère) - et les afficher dans la vue de dessus tout en changeant son opacité à 0. Possible mais beaucoup de travail.
David H
1
C'était le seul moyen (jusqu'à présent) que j'ai pu réaliser ce que je voulais dans iOS 10 - fondu en mode modal sur plein écran, avec un effet de flou très sombre. Merci!
Graham
3

Si vous voulez fondre dans votre UIVisualEffectView - pour ios10 utilisez 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]];
    }];

alors vous pouvez définir le pourcentage

[animator setFractionComplete:percent];

pour ios9, vous pouvez utiliser le composant alpha

kiril kiroski
la source
2
La question est spécifiquement étiquetée «swift» et non «objective-c»: veuillez fournir une réponse en Swift. Merci!
Eric Aya
1
Merci pour le code Objective C. Je l'ai trouvé pratique car il n'y avait pas beaucoup d'autres exemples répertoriés.
Adam Ware
2

L'alpha de l'UIVisualEffectView doit toujours être 1. Je pense que vous pouvez obtenir l'effet en définissant l'alpha de la couleur d'arrière-plan.

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

Peter Lendvay
la source
Peter, pouvez-vous expliquer comment définir l'alpha de la couleur d'arrière-plan?
Boris Y.
@borisy utilise la colorWithAlphaComponent:méthode sur les UIColorinstances. Vous ne savez pas comment obtenir le même effet en définissant la couleur d'arrière-plan.
nemesis
En changeant le composant alpha de fond ne fonctionne pas comme prévu
Honghao Zhang
1

Je crée un uiview dont l'alpha est 0 et j'ajoute blurview en tant que sous-vue. Ainsi, je peux masquer / afficher ou arrondir les coins avec une animation.

Kemal Can Kaynak
la source
1

J'ai fini avec la solution suivante, en utilisant des animations séparées pour le UIVisualEffectViewet le contenu. J'ai utilisé la viewWithTag()méthode pour obtenir une référence à l' UIViewintérieur du fichier 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 }

Je préférerais que l'animation unique change l'alpha, mais cela évite l'erreur et semble fonctionner aussi bien.

demian
la source
1

Je viens d'avoir ce problème et la façon dont je l'ai contourné était de loger l'UIVisualEffectsView dans un UIView et d'animer l'alpha de cet UIView.

Cela a bien fonctionné, sauf que dès que l'alpha a changé en dessous de 1.0, il est devenu un blanc uni et a l'air très discordant. Pour contourner ce problème, vous devez définir la propriété de couche de l'UIView containerView.layer.allowsGroupOpacity = falseet cela l'empêchera de clignoter en blanc.

Vous pouvez maintenant animer / fondre UIView contenant la vue des effets visuels et toutes les autres sous-vues en utilisant sa propriété alpha sans avoir à vous soucier des problèmes graphiques ou de la journalisation d'un message d'avertissement.

user1898712
la source
0
_visualEffectView.contentView.alpha = 0;

Pour changer l'alpha de UIVisualEffectView, vous devez changer le contentView de _visualEffectView.Si vous changez l'alpha de _visualEffectView, vous obtiendrez ceci

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

Habituellement, je souhaite animer un flou uniquement lorsque je présente un contrôleur de vue sur l'écran et que je souhaite brouiller le contrôleur de vue de présentation. Voici une extension qui ajoute blur () et unblur () à un contrôleur de vue afin de faciliter cela:

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

Vous pouvez bien sûr rendre cela plus robuste en laissant l'utilisateur choisir le style d'effet, modifier la durée, appeler quelque chose lorsque l'animation est terminée, marquer la vue d'effet visuel ajouté dans blur () pour vous assurer que c'est le seul supprimé lorsque vous débloquez ( ), etc., mais je n'ai pas trouvé le besoin de faire ces choses jusqu'à présent, car cela a tendance à être un type d'opération "feu et oublie".

cc.
la source
"feu et oublie" est si relatable! Ha
LinusGeffarth
0

basé sur la réponse de @ cc, j'ai modifié son extension pour brouiller une vue

extension UIView {

    func blur () {
        // Estompe la vue actuelle
        laissez blurView = UIVisualEffectView (frame: self.bounds)
        self.addSubview (blurView)
        UIView.animate (avec Durée: 0,25) {
            blurView.effect = UIBlurEffect (style: .dark)
        }
    }

    func unblur () {
        pour childView dans les sous-vues {
            guard laissez effectView = childView as? UIVisualEffectView else {continue}
            UIView.animate (avec Durée: 2.5, animations: {
                effectView.effect = nul
            }) {
                didFinish in
                effectView.removeFromSuperview ()
            }
        }
    }
}
Tel4tel
la source
0

Amélioration de la réponse @ Tel4tel et @cc, voici une extension avec des paramètres et une brève explication.

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

Ensuite, vous pouvez l'utiliser comme: view.blur(duration: 5.0, effect: .light) ou view.unblur(duration: 3.0)

N'oubliez pas de NE PAS l'utiliser viewDidLoad()car cela remplacera l'animation. De plus, lors de l'exécution sur un simulateur, placez les graphiques au niveau supérieur pour pouvoir voir l'animation (Débogage> Remplacement de la qualité graphique> Haute qualité).

Rafael Díaz
la source