33

In Yosemite sidebars have a semitransparent "vibrant" background. How can I create a view like that in 10.10/Xcode 6?

Can I give any view such background? I've found that NSOutlineView will default to such background when you give it "Source list" highlight style, but the sidebar in the Calendar.app doesn't appear to be an NSOutlineView, so I wonder if there's a generic solution for this.

enter image description here

Kornel
  • 97,764
  • 37
  • 219
  • 309

4 Answers4

45

w00t! I've found example code that uses not-yet-documented view type:

  1. Set XIB's deployment target to 10.10
  2. Embed your view in NSVisualEffectView
  3. In Interface Builder's settings for the view set appearance to "Vibrant Light/Dark". There are other options, like blending "Behind Window" or "Within Window" (the latter requires layers).

There's also NSView method allowsVibrancy that you can override to return YES, but for reasons I don't yet understand this didn't enable vibrancy in my case.

Stan James
  • 2,535
  • 1
  • 28
  • 35
Kornel
  • 97,764
  • 37
  • 219
  • 309
  • 5
    `allowsVibrancy` is used to change how a view draws when it's in a view hierarchy that uses vibrancy; it doesn't do the vibrant background itself. – Wevah Aug 22 '14 at 23:10
  • 1
    For other coders looking for a < 10.10 solution (like 10.8+), I haven't found any that does the diffusion/vibrancy after 6 hours of hunting and trying. I can only get see-through translucency, and [was able to achieve it partially on a window](http://stackoverflow.com/a/34525808/105539), rather than the whole window. I can, however, add a semi-transparent color, but gaussian blur seems to have no effect in providing behind-window style diffusion/vibrancy -- everything appears legible behind the window, just dimmer from the semi-transparent color. – Volomike Dec 31 '15 at 02:06
  • The answer from @ConfusedVorlon does the same thing as this except in code, and allows a graceful fallback so that people who need to make their build run on pre-10.10 OSX can have one code base (without a fork) that will enable the feature on Yosemite and greater, but disable the feature on earlier than Yosemite. – Volomike Jan 05 '16 at 20:24
  • I had the "Reduce Transparency" turned off in System Preferences/Accessibility. Nothing worked until I figured out after hours of trial and error – Tibidabo Apr 22 '16 at 05:57
20

With the introduction of the Yosemite version of the OSX operating system, Apple introduced a new mode called vibrancy, which is a light diffusion blur, to Cocoa window and window components. It's sort of like looking through a shower door, and uses the NSVisualEffectView. Apple explains this effect here.

I use this category on NSView. Simply call on the view you want to make vibrant. It is also backwards compatible with pre-Yosemite. (If you have a pre-Yosemite, you won't see the effect)

@implementation NSView (HS)

-(instancetype)insertVibrancyViewBlendingMode:(NSVisualEffectBlendingMode)mode
{
    Class vibrantClass=NSClassFromString(@"NSVisualEffectView");
    if (vibrantClass)
    {
        NSVisualEffectView *vibrant=[[vibrantClass alloc] initWithFrame:self.bounds];
        [vibrant setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
        // uncomment for dark mode instead of light mode
        // [vibrant setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]];
        [vibrant setBlendingMode:mode];
        [self addSubview:vibrant positioned:NSWindowBelow relativeTo:nil];
        
        return vibrant;
    }

    return nil;
}

@end

Detailed instructions from @Volomike follow…

How To Use

  1. Add AppKit.framework to your Project Settings > Build Phases > Link Binary with Libraries so that it can recognize NSVisualEffectView.

  2. Make an outlet delegate of your main window's default view, not the window itself, to your AppDelegate.m or AppDelegate.mm file. (If you're new at that, read this tutorial.) For purposes here, let's assume you named this as mainview, which then is addressable in code as _mainview.

  3. Include the category in your project. If you're new at that, add the category before any @implementation line in your AppDelegate.m or AppDelegate.mm file.

  4. In your AppDelegate.m or AppDelegate.mm file, in your @implementation AppDelegate, inside your applicationDidFinishLaunching class method, add this line of code:

[_mainview insertVibrancyViewBlendingMode:NSVisualEffectBlendingModeBehindWindow];
  1. Now you need to learn how to add some code to give the other elements on your window, as well as the window itself, translucency. That translucency will allow this effect to show through into your window components as you require. This is explained here.

The net effect now is that either the entire window below the titlebar, or only parts you want (such as a sidebar), will show this vibrancy effect.

Community
  • 1
  • 1
Confused Vorlon
  • 9,659
  • 3
  • 46
  • 49
  • 1
    I'm sorry but how can this be accomplished in a pre yosemite build? I can't even get to compile it. – superandrew Nov 17 '14 at 10:53
  • Placing the FX view below the content view leads to strange font rendering artifacts (the smoothing is done against the wrong background). I have found it better to _embed_ the content view inside the FX view. – zoul Mar 23 '15 at 10:34
  • @zoul any chance of a screenshot? I'm interested to see the difference with font rendering. (I'm not quite clear what the issue is) – Confused Vorlon Mar 24 '15 at 11:47
  • I have already nuked my sample project, but there are some screenshots of a related issue in [this thread](https://devforums.apple.com/message/1057745) at Apple Developer Forums. – zoul Mar 24 '15 at 13:49
  • @ConfusedVorlon can you explain how to use this in a pre Yosemite build? I'm running XCode7 and have set my Deployment Target to 10.8. – Volomike Jan 04 '16 at 21:59
  • @Volomike I'm not sure what there is to explain! It should just do nothing when run on a pre-Yosemite machine. – Confused Vorlon Jan 04 '16 at 22:17
  • @ConfusedVorlon Oh I get it! So, your code means that I can compile with XCode7 and set Deployment Target to 10.8, and if they have 10.10, the code will work, but if less than 10.10, it will still compile but just not show the effect. – Volomike Jan 04 '16 at 22:45
  • @ConfusedVorlon I enhanced your answer with some editing to explain a bit more how to activate it in one's project. – Volomike Jan 05 '16 at 18:40
  • 1
    @Volomike thanks for editing. I tweaked it a little to separate the answer and your tutorial. I wouldn't normally explain concepts like a category, or how to get a reference to your view (IB Outlets are only one way) - but if that is helpful for folks, it can stay at the bottom. – Confused Vorlon Jan 06 '16 at 10:05
  • How do to get the vibrancy on NSToolbar? I thought it's a subclass of NSView but the insertVibrancyViewBlendingMode method didn't work on NSToolbar. – Arunabh Das Jan 09 '16 at 15:48
  • Is any example in swift? – Juanjo Jan 24 '16 at 03:44
2

Simply use an NSVisualEffectView. You can further tweak it with its fields like so:

class MyFancyView: NSVisualEffectView {
    func myConfigureFunc() {

        // Use blendingMode to specify what exactly is blurred

        blendingMode = .behindWindow // [DEFAULT] ignores in-window content and only blurs content behind the window
        blendingMode = .withinWindow // ignores content behind the window and only blurs in-window content behind this view


        // Use material to specify how the blur draws (light/dark/etc.)

        material = .light           // The Vibrant Light look we see in countless Apple apps' sidebars, Sierra notification center, etc.
        material = .dark            // The Vibrant Dark look we all know and love from HUDs, Launchpad, Yosemite & El Capitan notification center, etc.

        material = .appearanceBased // [DEFAULT] Automatically uses .light or .dark, depending on the view's appearance field

        material = .titlebar        // The material the system uses in titlebars. Designed to be used with blendingMode = .withinWindow
        material = .selection       // A special material for selection. The material will vary depending on the effectiveAppearance, active state, and emphasized state.

        if #available(OSX 10.11, *) {

            // Materials introduced in 10.11 (El Capitan)

            material = .mediumLight // Not quite as light as .light
            material = .ultraDark   // Much darker than .dark

            material = .menu        // The material the system uses for menus
            material = .popover     // The material the system uses for popovers
            material = .sidebar     // The material the system uses for sidebars
        }


        // Use state to specify when the visual effect appears

        state = .active                   // Always show the visual effect
        state = .inactive                 // Never show the visual effect (behaves like a normal view)
        state = .followsWindowActiveState // [DEFAULT] Active when window is active, not when window is not
    }
}

Learn more by watching the official Apple video: WWDC 2014 Session 220

Ky -
  • 30,724
  • 51
  • 192
  • 308
2

Solution for SwiftUI:


import Foundation
import SwiftUI
import Combine

@available(OSX 11.0, *)
public extension View {
    func backgroundGaussianBlur(type: NSVisualEffectView.BlendingMode = .withinWindow) -> some View {
        self
            .background( VisualEffectView(type: type) )
    }
}

@available(OSX 10.15, *)
public struct VisualEffectView: NSViewRepresentable {
    let type: NSVisualEffectView.BlendingMode
    
    public init(type: NSVisualEffectView.BlendingMode = .withinWindow) {
        self.type = type
    }
    
    public func makeNSView(context: Context) -> NSVisualEffectView {
        NSVisualEffectView()
    }
    
    public func updateNSView(_ nsView: NSVisualEffectView, context: Context) {
        nsView.blendingMode = type
        nsView.material = .popover
    }
    
    public typealias NSViewType = NSVisualEffectView
}

usage:

SomeWindowView()
    .backgroundGaussianBlur(type: .behindWindow)
Andrew_STOP_RU_WAR_IN_UA
  • 9,318
  • 5
  • 65
  • 101