Fix UIVisualEffectView extra light blur being gray on white background
Asked Answered
P

9

23

Apple provides this live blurring view class UIVisualEffectView and you use it with a UIBlurEffect which takes one of three available UIBlurEffectStyles:

enum UIBlurEffectStyle : Int {
    case ExtraLight
    case Light
    case Dark
}

Now for demo purposes I set up two effect views with the styles Light and ExtraLight:

let lightBlur = UIBlurEffect(style: UIBlurEffectStyle.Light)
let extraLightBlur = UIBlurEffect(style: UIBlurEffectStyle.ExtraLight)

let lightView = UIVisualEffectView(effect: lightBlur)
lightView.frame = CGRectMake(10, 30, 150, 150)
self.view.addSubview(lightView)

let extraLightView = UIVisualEffectView(effect: extraLightBlur)
extraLightView.frame = CGRectMake(160, 30, 150, 150)
self.view.addSubview(extraLightView)

So far so good, everything works as expected, they blur images:

image

and kind of work on colors, too:

black red

but when it comes to a white background this happens:

white

The Light effect on the left works as expected, but the ExtraLight effect on the right leaves some kind of gray square behind.

Now the question: Is there any kind of trick or method that would enable me to use an extra light blur effect on white background (with live blurring support) AND remove that ugly gray shadow?

Posting answered 22/7, 2016 at 7:36 Comment(3)
Did you find a solution to this?Olericulture
The same issue applies to UIBlurEffectStyle.Dark on a black background: it will appear gray instead of black on a real device.Pathless
Any solution to this problem?Grouch
P
17

As far as I know the additional tint is a built-in feature of the UIVisualEffectView class. If you examine the view hierarchy with Xcode, you can see that there are two default subviews in the visual effect view instance: UIVisualEffectBackdropView and UIVisualEffectSubview. (I assume that these are private classes.) If you inspect UIVisualEffectSubview you can see that it has a background color which causes the unwanted tint that you've noticed.

I am not sure if there's an officially supported way to remove it, but you can modify this background color by filtering to the name of the private subview:

if let vfxSubView = visualEffectView.subviews.first(where: {
    String(describing: type(of: $0)) == "_UIVisualEffectSubview"
}) {
    vfxSubView.backgroundColor = UIColor.white.withAlphaComponent(0.7)
}

(Swift 5 compatible)

The default tint is around 70% opacity, so the easiest is to use the target background color with 0.7 alpha component.

I've also noticed that this might reset to the default value if the visual effect contains a custom subview. If I add this same snippet to the viewDidLayoutSubviews function of the view's controller, then it will keep the custom background color even after the built-in subview is updated.

Here's an example with a dark blur effect style. The top part shows the default tint and the bottom version has a custom black background color with 70% opacity.

enter image description here

Polymyxin answered 10/2, 2020 at 12:4 Comment(4)
I don't particularly like manipulating the hidden iOS subviews, but this method seems safe enough and it actually works!Panelist
Is there a chance the app gets rejected by Apple if using the first solution ? This is now a "private" API I guess ?..Guildsman
@JeremLachkar I don't think they would reject it. Yes the UIVisualEffectSubview is a private type, but this snippet doesn't reference that symbol, it only checks the string description of the view and I doubt that Apple is considering those in the review process. Also I've delivered a couple apps with this code without rejection.Polymyxin
Do you think this will work for macOS as well? ThanksHaploid
A
3

If you just want the blur and your blurred view is gonna be stationary, you could use the UIImageEffects class and change the tintColor to a "full" white:

- (UIImage *)applyExtraLightEffect
{
    UIColor *tintColor = [UIColor colorWithWhite:0.97 alpha:0.82];
    return [self applyBlurWithRadius:20 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil];
}

As far as I know you can't change it in the UIVisualEffectView.

Alroi answered 22/7, 2016 at 9:31 Comment(1)
Yes, that resolves the gray shadow problem, but unfortunately this method is not fast enough to create a smooth live blur when I scroll stuff behind the blur view...Posting
O
3

You can try :

var visualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .Light))    
visualEffectView.frame = imageView.bounds
imageView.addSubview(visualEffectView)
Oldtimer answered 29/7, 2016 at 9:36 Comment(1)
Well yes, but I'd like to use the Extra Light effectPosting
E
2

The following code should do the trick to give it a the desired tint:

let visualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .regular))
visualEffectView.subviews.forEach { subview in
    subview.backgroundColor = UIColor.blue.withAlphaComponent(0.2)
}

Important Note:

I wouldn't recommend adding a check for "_UIVisualEffectSubview" since this class can change on subsequent iOS updates. Also, there is a possibility of app getting rejected because of this.

Esmeralda answered 21/9, 2021 at 16:52 Comment(0)
F
1

iOS 15 has the nice

.background(.ultraThinMaterial)

but it still suffers from the nasty grey tint. Wish there was an option to blur background views without any tint.

I tried a simple color-correct hack which worked for my background color but only in light mode! For dark mode I just made the background solid black (no translucent blur)

.background(
    // negate the slight grey tint of ultrathinmaterial
    Color("materialTintColorCorrect")
        .background(.ultraThinMaterial)
)

My "materialTintColorCorrect" color asset was #F0F8FF 27% opacity for light mode and #000000 100% opacity for dark.

Fall answered 6/1, 2023 at 4:56 Comment(0)
G
0

Simple Solution

I could find a simple solution inspired by Shannon Hughes' Blog Post. Just add a white background color with transparency to the effect view. I don't know if it is exactly like extraLight but for me it is close enough.

let visualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .light))
visualEffectView.frame = sectionHeaderView.bounds
visualEffectView.backgroundColor = UIColor(white: 1.0, alpha: 0.9)
sectionHeaderView.addSubview(visualEffectView)
Gundry answered 8/3, 2017 at 17:5 Comment(0)
Q
-1
let vc = UIViewController()
vc.view.frame = self.view!.frame
let efv = UIVisualEffectView(effect: UIBlurEffect(style: UIBlurEffectStyle.light))
efv.frame = vc.view.frame
vc.view.addSubview(efv)
self.addChildViewController(vc)
self.view.addSubview(vc.view)
// below method has a bug
// self.present(vc, animated: true, completion:nil)
Quadrangular answered 25/8, 2016 at 3:15 Comment(1)
I can not see how this will result in the desired Extra Light behavior.Posting
L
-1

I would recommend adding your extraLightView to a view of UIColor(white: 1.0, alpha: 0.x), where x can be modified based on the scroll view's contentOffset. When there's nothing behind the view, your extraLightView will be white when x is 1. When you scroll and modify x, you won't be modifying the UIVisualEffectView (which is highly discouraged), but rather its parent view, which is perfectly safe.

Lukasz answered 18/7, 2019 at 1:3 Comment(0)
H
-1

Works with Swift 5

My way of making the visual effect view completely white when the background view is white.

let blurView = UIVisualEffectView(effect: UIBlurEffect(style: .light))
blurView.backgroundColor = UIColor.white.withAlphaComponent(0.7)
Humanity answered 5/8, 2019 at 12:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.