fabian
fabian

Reputation: 5463

How to set the BlurRadius of UIBlurEffectStyle.Light

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

Upvotes: 69

Views: 41709

Answers (13)

AngelSenpai
AngelSenpai

Reputation: 31

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

Upvotes: 3

jjjjjjjj
jjjjjjjj

Reputation: 4493

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
    }

Upvotes: 0

Siempay
Siempay

Reputation: 1002

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)

Upvotes: 0

Yansong
Yansong

Reputation: 11

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 :-)

Upvotes: 1

Jack Guo
Jack Guo

Reputation: 4684

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://stackoverflow.com/a/51603339/3808183)

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)

Upvotes: 6

Darrarski
Darrarski

Reputation: 4032

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

Upvotes: 86

Luca Davanzo
Luca Davanzo

Reputation: 21520

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

Upvotes: 0

k06a
k06a

Reputation: 18785

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.

Upvotes: 18

Demilitarized Zone
Demilitarized Zone

Reputation: 319

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

Upvotes: -2

Serg Dort
Serg Dort

Reputation: 487

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)

Upvotes: -6

Kwalker108
Kwalker108

Reputation: 492

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.

Upvotes: 28

Eugene Dudnyk
Eugene Dudnyk

Reputation: 6030

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.

Upvotes: 21

Louis Zhu
Louis Zhu

Reputation: 802

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...

Upvotes: 4

Related Questions