How to set the BlurRadius of UIBlurEffectStyle.Light
Asked Answered
B

13

68

I was wondering how to set the radius/blur factor of iOS new UIBlurEffectStyle.Light? I could not find anything in the documentation. But I want it to look similar to the classic UIImage+ImageEffects.h blur effect.

required init(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    
    let blur = UIBlurEffect(style: UIBlurEffectStyle.Light)
    let effectView = UIVisualEffectView(effect: blur)
    effectView.frame = frame
    addSubview(effectView)
}
Biometry answered 27/8, 2014 at 14:13 Comment(0)
V
82

Changing alpha is not a perfect solution. It does not affect blur intensity. You can setup an animation from nil to target blur effect and manually set time offset to get desired blur intensity. Unfortunately iOS will reset the animation offset when app returns from background.

Thankfully there is a simple solution that works on iOS >= 10. You can use UIViewPropertyAnimator. I didn't notice any issues with using it. I keeps custom blur intensity when app returns from background. Here is how you can implement it:

class CustomIntensityVisualEffectView: UIVisualEffectView {

    /// Create visual effect view with given effect and its intensity
    ///
    /// - Parameters:
    ///   - effect: visual effect, eg UIBlurEffect(style: .dark)
    ///   - intensity: custom intensity from 0.0 (no effect) to 1.0 (full effect) using linear scale
    init(effect: UIVisualEffect, intensity: CGFloat) {
        super.init(effect: nil)
        animator = UIViewPropertyAnimator(duration: 1, curve: .linear) { [unowned self] in self.effect = effect }
        animator.fractionComplete = intensity
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError()
    }

    // MARK: Private
    private var animator: UIViewPropertyAnimator!

}

I also created a gist: https://gist.github.com/darrarski/29a2a4515508e385c90b3ffe6f975df7

Vamoose answered 24/11, 2017 at 14:51 Comment(7)
This is be the accepted answer. Working like a charm! thanksUstkamenogorsk
Exploiting the fraction complete is such a smart solution. As far as I know, this is the only way to do a proper gradual blur using the UIBlurEffect. Changing the alpha of a blur view breaks the effect and SHOULD NOT be used.Tuesday
Works perfectly on iOS 13.4 too! Great job.Maynor
Works great! For some reason whenever I try and do this manually myself I always get a crash to do with the animation being released. Anyway - this would be even better if there was a way of slowing down the transition! Any ideas on how to do that?Landgraviate
Just a follow-up to my comment: I've found a (relatively dodgy) way of doing this by implementing the Interpolate library. This doesn't seem to work with UIViewPropertyAnimators for some reason. Just add an extra argument to the init for duration, then replace 'animator.fractionComplete = intensity' with the following: let interpolateAnimation = Interpolate(from: 0, to: intensity) { (destination) in self.animator.fractionComplete = CGFloat(destination) print(self.animator.fractionComplete) } interpolateAnimation.animate(duration: duration)Landgraviate
I've used this method in viewDidLoad, a key factor to make it work is to keep a strong reference to the animator.Brake
Add animator.pausesOnCompletion = true to get consistent results. Otherwise the animation could get reset.Jeweller
B
28

You can change the alpha of the UIVisualEffectView that you add your blur effect to.

let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.Light)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
blurEffectView.alpha = 0.5
blurEffectView.frame = self.view.bounds
self.view.addSubview(blurEffectView)

This is not a true solution, as it doesn't actually change the radius of the blur, but I have found that it gets the job done with very little work.

Barham answered 8/8, 2015 at 16:49 Comment(2)
While not addressing the core problem, this is certainly a very nice workaround and more likely to be accepted into the store! Nice one.Dewittdewlap
When using the UIVisualEffectView class, avoid alpha values that are less than 1. Creating views that are partially transparent causes the system to combine the view and all the associated subviews during an offscreen render pass. UIVisualEffectView objects need to be combined as part of the content they are layered on top of in order to look correct. 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. developer.apple.com/library/tvos/documentation/UIKit/Reference/…Gingerich
C
21

Although it is a hack and probably it won't be accepted in the app store, it is still possible. You have to subclass the UIBlurEffect like this:

#import <objc/runtime.h>

@interface UIBlurEffect (Protected)
@property (nonatomic, readonly) id effectSettings;
@end

@interface MyBlurEffect : UIBlurEffect
@end

@implementation MyBlurEffect

+ (instancetype)effectWithStyle:(UIBlurEffectStyle)style
{
    id result = [super effectWithStyle:style];
    object_setClass(result, self);

    return result;
}

- (id)effectSettings
{
    id settings = [super effectSettings];
    [settings setValue:@50 forKey:@"blurRadius"];
    return settings;
}

- (id)copyWithZone:(NSZone*)zone
{
    id result = [super copyWithZone:zone];
    object_setClass(result, [self class]);
    return result;
}

@end

Here blur radius is set to 50. You can change 50 to any value you need.

Then just use MyBlurEffect class instead of UIBlurEffect when creating your effect for UIVisualEffectView.

Cytokinesis answered 9/6, 2015 at 23:45 Comment(7)
I swear they announced at the platforms state of the union that you can modify the blurRadius! But its not in any docs that I could find!Burseraceous
This sholution doesn't work. After changing blurRadius error occures: malloc: *** error for object 0x7fbf9969b690: incorrect checksum for freed object - object was probably modified after being freed. *** set a breakpoint in malloc_error_break to debugCapernaum
Works perfectly if to do it exactly how it is written in the answer. If you modify blur radius after adding the BlurEffect onto the Effect View - you are doing wrong. The BlurEffect is copied during adding, and the instance you are modifying is not used by an EffectView.Cytokinesis
Is only one take this answer and accepted in the app store?Scotney
this works, but it doesn't show gradual changes in blur radius going - let's say - from radius 1 to 5 or 10... (at least in the simulator)Somnambulism
Do you mean you want to animate radius change?Cytokinesis
no, I don't mean animation, just noticed that the blur doesn't seems to be applied linearly with the set value. Example: when I apply radius 2 it doesn't look visually different than when I apply a radius 10 or so.Somnambulism
P
18

Recently developed Bluuur library to dynamically change blur radius of UIVisualEffectsView without usage any of private APIs: https://github.com/ML-Works/Bluuur

It uses paused animation of setting effect to achieve changing radius of blur. Solution based on this gist: https://gist.github.com/n00neimp0rtant/27829d87118d984232a4

And the main idea is:

// Freeze animation
blurView.layer.speed = 0;

blurView.effect = nil;
[UIView animateWithDuration:1.0 animations:^{
    blurView.effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
}];

// Set animation progress from 0 to 1
blurView.layer.timeOffset = 0.5;

UPDATE:

Apple introduced UIViewPropertyAnimator class in iOS 10. Thats what we need exactly to animate .effect property of UIVisualEffectsView. Hope community will be able to back-port this functionality to previous iOS version.

Peonage answered 3/6, 2016 at 11:57 Comment(3)
This should really be the accepted answer. Currently accepted one goes directly against Apple's guidelines.Kumiss
Since December 2016 and the commit github.com/ML-Works/Bluuur/commit/… the Bluuur project does use private API to change the blurRadius! See this issue: github.com/ML-Works/Bluuur/issues/17Ambulatory
@Ambulatory thanks! This approach works more stable especially with animations.Peonage
L
6

This is totally doable. Use CIFilter in CoreImage module to customize blur radius. In fact, you can even achieve a blur effect with continuous varying (aka gradient) blur radius (https://mcmap.net/q/282120/-apply-gradient-effect-to-a-blur-view)

import CoreImage

let ciContext = CIContext(options: nil)

guard let inputImage = CIImage(image: yourUIImage), 
    let mask = CIFilter(name: "CIGaussianBlur") else { return }
mask.setValue(inputImage, forKey: kCIInputImageKey)
mask.setValue(10, forKey: kCIInputRadiusKey) // Set your blur radius here

guard let output = mask.outputImage,
    let cgImage = ciContext.createCGImage(output, from: inputImage.extent) else { return }
outUIImage = UIImage(cgImage: cgImage)
Lactoscope answered 31/7, 2018 at 1:18 Comment(1)
Lest we forget CoreImageObturate
D
4

I'm afraid there's no such api currently. According to Apple's way of doing things, new functionality was always brought with restricts, and capabilities will bring out gradually. Maybe that will be possible on iOS 9 or maybe 10...

Doorpost answered 30/9, 2014 at 3:58 Comment(12)
True. I mean we didn't even have a UIVisualEffectView before, so things are slowly improving.Hunfredo
Hello from 2016. iOS 9.2 is out and still no blurRadius.Irrelievable
iOS10? Still no ¯_(ツ)_/¯Tamikatamiko
iOS 11? Orz===3Deirdredeism
iOS12? Still no ¯\_(ツ)_/¯Yukyukaghir
iOS13? Still no ¯\_(ツ)_/¯Yukyukaghir
iOS 13 - blur(radius:opaque:), for SwiftUIMarmoset
iOS 14? Still no, tomorrow iOS 15 will be released probably still no..Millepede
iOS15... Still no ¯_(ツ)_/¯Angiology
iOS16... Still no ¯\_(ツ)_/¯Anselma
I'm not the author of the pod, but this one still works as of iOS16 and down. Spent ages looking for alternatives, and other suggestions around arresting the animation at some % in seem to have followups with crashes etc. FWIW: cocoapods.org/pods/DynamicBlurViewNeuburger
iOS 17? Still noBugger
I
3

I have ultimate solution for this question:

fileprivate final class UIVisualEffectViewInterface {
func setIntensity(effectView: UIVisualEffectView, intensity: CGFloat){
    let effect = effectView.effect
    effectView.effect = nil
    animator = UIViewPropertyAnimator(duration: 1, curve: .linear) { [weak effectView] in effectView?.effect = effect }
    animator.fractionComplete = intensity
}

private var animator: UIViewPropertyAnimator! }



extension UIVisualEffectView{
private var key: UnsafeRawPointer? { UnsafeRawPointer(bitPattern: 16) }

private var interface: UIVisualEffectViewInterface{
    if let key = key, let visualEffectViewInterface = objc_getAssociatedObject(self, key) as? UIVisualEffectViewInterface{
        return visualEffectViewInterface
    }
    let visualEffectViewInterface = UIVisualEffectViewInterface()
    
    if let key = key{
        objc_setAssociatedObject(self, key, visualEffectViewInterface, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
    }
    return visualEffectViewInterface
}

func intensity(_ value: CGFloat){
    interface.setIntensity(effectView: self, intensity: value)
}}
Iconology answered 27/8, 2020 at 10:40 Comment(2)
Does this use any private APIs? Has someone been successful in getting this approved on the App Store?Datolite
@Datolite , i have a work app in apple store with this solutionIconology
M
1

This idea hits me after tried the above solutions, a little hacky but I got it working. Since we cannot modify the default radius which is set as "50", we can just enlarge it and scale it back down.

previewView.snp.makeConstraints { (make) in
    make.centerX.centerY.equalTo(self.view)
    make.width.height.equalTo(self.view).multipliedBy(4)
}

previewBlur.snp.makeConstraints { (make) in
    make.edges.equalTo(previewView)
}

And then,

previewView.transform = CGAffineTransform(scaleX: 0.25, y: 0.25)
previewBlur.transform = CGAffineTransform(scaleX: 0.25, y: 0.25)

I got a 12.5 blur radius. Hope this will help :-)

Municipal answered 10/1, 2019 at 7:15 Comment(1)
Top! Works brilliant, no private api!Curate
C
0

Currently I didn't find any solution.
By the way you can add a little hack in order to let blur mask less "blurry", in this way:

let blurView = .. // here create blur view as usually

if let blurSubviews = self.blurView?.subviews {
    for subview in blurSubviews {
        if let filterView = NSClassFromString("_UIVisualEffectFilterView") {
            if subview.isKindOfClass(filterView) {
                subview.hidden = true
            }
        }
    }
}
Cristoforo answered 24/10, 2016 at 13:27 Comment(0)
D
0

for iOS 11.*

in viewDidLoad()

    let blurEffect = UIBlurEffect(style: .dark)
    let blurEffectView = UIVisualEffectView()
    view.addSubview(blurEffectView)

    //always fill the view
    blurEffectView.frame = self.view.bounds
    blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    UIView.animate(withDuration: 1) {
        blurEffectView.effect = blurEffect
    }
    blurEffectView.pauseAnimation(delay: 0.5)
Dwanadwane answered 29/3, 2019 at 11:34 Comment(0)
A
0

There is an undocumented way to do this. Not necessarily recommended, as it may get your app rejected by Apple. But it does work.

if let blurEffectType = NSClassFromString("_UICustomBlurEffect") as? UIBlurEffect.Type {
        let blurEffectInstance = blurEffectType.init()

        // set any value you want here. 40 is quite blurred 
        blurEffectInstance.setValue(40, forKey: "blurRadius")
        let effectView: UIVisualEffectView = UIVisualEffectView(effect: blurEffectInstance)

        // Now you have your blurred visual effect view
    }
Aetna answered 5/3, 2020 at 9:30 Comment(1)
broken on iOS 14. Now just desaturates without blurringEggplant
U
-2

This works for me.

I put UIVisualEffectView in an UIView before add to my view.

I make this function to use easier. You can use this function to make blur any area in your view.

func addBlurArea(area: CGRect) {
    let effect = UIBlurEffect(style: UIBlurEffectStyle.Dark)
    let blurView = UIVisualEffectView(effect: effect)
    blurView.frame = CGRect(x: 0, y: 0, width: area.width, height: area.height)

    let container = UIView(frame: area)
    container.alpha = 0.8
    container.addSubview(blurView)

    self.view.insertSubview(container, atIndex: 1)
}

For example, you can make blur all of your view by calling:

addBlurArea(self.view.frame)

You can change Dark to your desired blur style and 0.8 to your desired alpha value

Unreflecting answered 29/10, 2015 at 23:8 Comment(2)
this doesn't answer the question at all. OP asked how to change blur radius, not how to add UIVisualEffectView to your appGomer
Yes, it is. The blur radius value is directly related to alpha value I mentioned in the answer.Unreflecting
K
-6

If you want to accomplish the same behaviour as iOS spotlight search you just need to change the alpha value of the UIVisualEffectView (tested on iOS9 simulator)

Kegler answered 21/8, 2015 at 10:25 Comment(1)
Changing the alpha doesn't give you the same result at all.. you have to change the blur radius to do that.Immoral

© 2022 - 2024 — McMap. All rights reserved.