Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adjust NSVisualEffectView blur radius and transparency

Tags:

macos

swift

cocoa

Is it possible to adjust the blur radius and transparency of an NSVisualEffectView when it's applied to an NSWindow (Swift or Objective-C)? I tried all variations of NSVisualEffectMaterial (dark, medium, light) - but that's not cutting it. In the image below I've used Apple's non-public API with CGSSetWindowBackgroundBlurRadius on the left, and NSVisualEffectView on the right.

enter image description here

I'm trying to achieve the look of what's on the left, but it seems I'm relegated to use the methods of the right.

Here's my code:

blurView.blendingMode = NSVisualEffectBlendingMode.BehindWindow
blurView.material = NSVisualEffectMaterial.Medium
blurView.state = NSVisualEffectState.Active
self.window!.contentView!.addSubview(blurView)

Possibly, related - but doesn't answer my question:

  • OS X NSVisualEffect decrease blur radius? - no answer
like image 750
Cocoa Puffs Avatar asked May 14 '16 20:05

Cocoa Puffs


2 Answers

Although I wouldn't recommend this unless you are ready to fall back to it not working in a future release, you can subclass NSVisualEffectView with the following to do what you want:

- (void)updateLayer
{
    [super updateLayer];

    [CATransaction begin];
    [CATransaction setDisableActions:YES];

    CALayer *backdropLayer = self.layer.sublayers.firstObject;

    if ([backdropLayer.name hasPrefix:@"kCUIVariantMac"]) {
        for (CALayer *activeLayer in backdropLayer.sublayers) {
            if ([activeLayer.name isEqualToString:@"Active"]) {
                for (CALayer *sublayer in activeLayer.sublayers) {
                    if ([sublayer.name isEqualToString:@"Backdrop"]) {
                        for (id filter in sublayer.filters) {
                            if ([filter respondsToSelector:@selector(name)] && [[filter name] isEqualToString:@"blur"]) {
                                if ([filter respondsToSelector:@selector(setValue:forKey:)]) {
                                    [filter setValue:@5 forKey:@"inputRadius"];
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    [CATransaction commit];
}

Although this doesn't use Private APIs per se, it does start to dig into layer hierarchies which you do not own, so be sure to double check that what you are getting back is what you expect, and fail gracefully if not. For instance, on 10.10 Yosemite, the Backdrop layer was a direct decedent of the Visual Effect view, so things are likely to change in the future.

like image 97
Dimitri Bouniol Avatar answered Nov 19 '22 22:11

Dimitri Bouniol


I had the same issue as you had and I have solved it with a little trick seems to do the job that I wanted. I hope that it will also help you.

So in my case, I have added the NSVisualEffectView in storyboards and set its properties as follows:

NSVisualEffectView setting and my View hierarchy is as follows:

View hierarchy

All that reduces the blur is in the NSViewController in:

override func viewWillAppear() {
    super.viewWillAppear()
    //Adds transparency to the app
    view.window?.isOpaque = false
    view.window?.alphaValue = 0.98 //tweak the alphaValue for your desired effect
}

Going with your example this code should work in addition with the tweak above:

    let blurView = NSVisualEffectView(frame: view.bounds)
    blurView.blendingMode = .behindWindow
    blurView.material = .fullScreenUI
    blurView.state = .active
    view.window?.contentView?.addSubview(blurView)

Swift 5 code For anyone interested here is a link to my repo where I have created a small prank app which uses the code above: GitHub link

like image 1
AD Progress Avatar answered Nov 19 '22 22:11

AD Progress