How to fade a UIVisualEffectView and/or UIBlurEffect in and out?
Asked Answered
P

15

95

I want to fade a UIVisualEffectsView with a UIBlurEffect in and out:

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

I use a normal animation within a function called by a UIButton to fade it in, same for fading out but .alpha = 0 & hidden = true:

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

Now, fading in both directions does work but it gives me an error when fading out:

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

Question

How do I successfully fade the UIVisualEffectView in and out without breaking it and having a fading transition?

Note

  • I tried to put the UIVisualEffectView into a UIView and fade that one, no success
Pastoral answered 27/3, 2015 at 18:33 Comment(2)
You can avoid the Error by: 1. NOT assigning an initial style to the blurEffect. i.e., var blurEffectView = UIVisualEffectView() and then 2. assigning the blurEffect to the blurEffectView INSIDE the animation block. 3. animating the "assignment of blurEffect to blurEffectView" is done in place of adjustments to blurEffectView.alpha. **The error will appear even if you assign an initial value, nil it, then add it back. So the key to preventing the error is to not assign the effect to blurEffectView until the animation block.Chutzpah
Also need to mention, the blurEffectView must have alpha=1.0 to avoid the error. Tampering with alpha in any way will produce the error. Eg, prior to animation block you set blurEffectView.alpha = 0.7, then in the animationBlock you assign blurEffectView.effect = blurEffect, the error is produced.Chutzpah
T
193

I think this is new in iOS9, but you can now set the effect of a UIVisualEffectView inside an animation block:

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

Set it to nil to remove.

VERY IMPORTANT - When testing this on the simulator, make sure to set your simulator's Graphics Quality Override to High Quality in order for this to work.

Tell answered 25/9, 2015 at 11:0 Comment(8)
You can also set the effect to nil to get a pretty "blur out"Jurist
@Jurist Would you mind explaining what you mean by "blur out?"Westmoreland
Any Idea how not to animated it but to set it to like 50% of the effect?Heredes
This doesn't work. When you play close attention, you see that the blur effect is only added at the end of the animation. A UIBlurEffect can't be animated as of iOS10.3. See andrewbuilder's answer below.Pacifically
@Pacifically it does work. Are you doing it on the simulator or a device (it looks terrible on the simulator)Tell
@Tell on the device. I am adding the UIVisualEffectsView to the view on viewDidLoad and add the effect property on viewWillAppear. I can see the animation darkening the background, until the final shade, then the blur is added in an instant.Pacifically
Then I'm not sure what's happening. I have it working in other projects. It's probably best to ask a new question and include your specific setup.Tell
@VyachaslavGerchicov It works beautifully. You're basically putting a blank UIVisualEffectView into a view and animating the effect, which is why you set the effect inside the animation block.Swane
J
19

The Apple documentation (currently) states...

When using the UIVisualEffectView class, avoid alpha values that are less than 1.

and

Setting the alpha to less than 1 on the visual effect view or any of its superviews causes many effects to look incorrect or not show up at all.

I believe some important context is missing here...

I'd suggest that the intent is to avoid alpha values that are less than 1 for a persistent view. In my humble opinion this does not apply to the animation of a view.

My point - I'd suggest that alpha values less than 1 are acceptable for animations.

The terminal message states:

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

Reading this carefully, the effect will appear to be broken. My points on this being:

  • the apparent break only really matters for a view that is persistent - not changing;
  • a persistent / unchanging UIVisualEffect view with an alpha value less than 1 will not present as intended / designed by Apple; and
  • the message in the terminal is not an error, just a warning.

To extend @jrturton's answer above that helped me solve my problem, I'd add...

To fade out the UIVisualEffect use the following (Objective-C) code:

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

I successfully use both methods: setting the effect property to nil and setting the alpha property to 0.

Note that setting the effect to nil creates a "nice flash" (for want of a better description) at the end of the animation, while setting the alpha to 0 creates a smooth transition.

(Let me know any syntax errors... I write in obj-c.)

Jeana answered 26/3, 2016 at 0:45 Comment(1)
Thanks for your contribution. I get your point; the error is resolved for me already w/ exactly what you mentioned :)Pastoral
J
15

Here is the solution that I ended up which works on both iOS10 and earlier versions using 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
            }
        }
    }

}

You can also check this gist to find an example usage.

Johniejohnna answered 16/2, 2017 at 13:53 Comment(2)
Using the UIViewPropertyAnimator is the only thing that seems to work in iOS 11. Thanks for this!Pastoral
Don't forget to add: import UIKitOneupmanship
R
8

Just a workaround - put UIVisualEffectView into a container view and change alpha property for that container. That approach works perfectly for me on iOS 9. Seems it no longer works in iOS 10.

Rob answered 9/7, 2015 at 13:43 Comment(7)
Can you provide sample code. This doesn't seem to work.Graner
Works for me in iOS 9Neurotic
@DerrickHunt Same here. Did you find a solution?Chapiter
@Chapiter If your project is iOS 10 only, you can use the new UIViewPropertyAnimator class to animate the UIBlurView strength. If you need to support older versions, you can use the above code and the use the #available keyword to use UIViewPropertyAnimator for devices running 10. Hope this helps!Bronchitis
@DerrickHunt Any Idea how not to animated it but to set it to like 50% of the effect?Heredes
@Heredes iOS 10 or lower?Bronchitis
iOS10... on < iOS it was working by changing the alpha of the container view... but now on iOS10 my interactive blurring is broken :(Heredes
E
5

You can change the alpha of the visual effects view without any problems, other than the warning in the console. The view may appear as simply partially transparent, rather than blurred. But this usually isn't a problem if you're just changing the alpha during animation.

Your app isn't going to crash or get rejected for this. Test it on a real device (or eight). If you're happy with how it looks and performs, it's fine. Apple's just warning you that it may not look or perform as well as a visual effects view with an alpha value of 1.

Erickericka answered 13/5, 2015 at 17:45 Comment(0)
A
5

You can take a snapshot of a static underlying view, and fade it in and out without touching the opacity of the blur view. Assuming an ivar of 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()
    } )
}
Annam answered 29/7, 2015 at 18:45 Comment(3)
Nice idea as well but that won't work well for non-static (moving / animating) content beneath the blur view.Pastoral
@LinusG. Well, Apple says shouldn't play around with a blur view alpha. You could use a CADisplayLink to grab screen shots of your base view, then render them in a image view (perhaps one you did - real lightweight) - and show those in the top view while changing its opacity to 0. Possible but a lot of work.Annam
This was the only way (so far) I've been able to achieve what I wanted in iOS 10 - fade in a modal over fullscreen, with a very dark blur effect. Thanks!Nims
C
3

If you want to fade in you UIVisualEffectView - for 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]];
    }];

then you can set percent

[animator setFractionComplete:percent];

for ios9 you can use alpha component

Charity answered 7/12, 2016 at 9:54 Comment(2)
The question is specifically tagged "swift", not "objective-c": please provide an answer in Swift. Thanks!Tortoni
Thanks for the Objective C Code. I found it handy as there wasn't many other examples listed.Scrim
L
2

The alpha of the UIVisualEffectView always has to be 1. I think you can achieve the effect by setting the alpha of the background color.

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

Linsang answered 27/3, 2015 at 18:53 Comment(3)
Peter, can you please explain how to set alpha of the background color?Microbarograph
@borisy use the colorWithAlphaComponent: method on the UIColor instances. Not sure how to achieve the same effect with setting the background color, though.Fled
By changing background alpha component is not working as expectedEntropy
K
1

I ended up with the following solution, using separate animations for the UIVisualEffectView and the contents. I used the viewWithTag() method to get a reference to the UIView inside the 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 }

I would prefer the single animation changing the alpha, but this avoids the error and seems to work just as well.

Kahler answered 6/11, 2015 at 2:13 Comment(0)
M
1

I make an uiview which alpha is 0 and add blurview as subview of that. So i can hide/show or rounding corners it with animation.

Meiosis answered 2/12, 2015 at 7:38 Comment(0)
H
1

I just had this problem and the way I got around it was to house the UIVisualEffectsView in a UIView, and animate that UIView's alpha.

This worked well, except that it as soon as the alpha changed below 1.0 it turned to a solid white and looked very jarring. In order to get around this, you must set the UIView's layer property containerView.layer.allowsGroupOpacity = false and this will prevent it from flashing white.

Now you can animate in/fade out the UIView containing the visual effects view and any other subviews using it's alpha property and not have to worry about any graphical glitches or it logging a warning message.

Henryk answered 13/6, 2017 at 23:38 Comment(0)
T
0
_visualEffectView.contentView.alpha = 0;

To change the alpha of UIVisualEffectView, you should change the contentView of _visualEffectView.If you change alpha of _visualEffectView, you will get this

<UIVisualEffectView 0x7ff7bb54b490> is being asked to animate its opacity. This will cause the effect to appear broken until opacity returns to 1.
Trine answered 29/3, 2016 at 9:53 Comment(0)
P
0

Usually, I only want to animate a blur when I'm presenting a view controller over the screen and want to blur the presenting view controller. Here's an extension that adds blur() and unblur() to a view controller in order to facilitate that:

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

You can of course make this more robust by letting the user choose the effect style, modify the duration, call something when the animation is completed, tag the added visual effect view in blur() to ensure it's the only one removed when you unblur(), etc., but I haven't found the need to do these things so far, since this tends to be a "fire and forget" type of operation.

Precursory answered 30/5, 2017 at 18:44 Comment(1)
"fire and forget" is so relatable! HaPastoral
P
0

based on @cc's answer i modified his extension to blur a view

extension UIView {

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

    func unblur() {
        for childView in subviews {
            guard let effectView = childView as? UIVisualEffectView else { continue }
            UIView.animate(withDuration: 2.5, animations: {
                effectView.effect = nil
            }) {
                didFinish in
                effectView.removeFromSuperview()
            }
        }
    }
}
Persson answered 30/8, 2018 at 12:45 Comment(0)
K
0

Improving @Tel4tel and @cc response, here is an extension with parameters and a brief explanation.

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

Then you can use it like: view.blur(duration: 5.0, effect: .light) or view.unblur(duration: 3.0)

Remember to NOT use it in viewDidLoad() as it will override the animation. Also, when running on a Simulator, turn the graphics to the Higher level to be able to see the animation (Debug > Graphics Quality Override > High Quality).

Kasper answered 13/9, 2018 at 9:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.