macoscocoaappkitnsscrollviewnsvisualeffectview

How to prevent macOS from inserting an NSVisualEffectView into an NSScrollView automatically


I'm building a NSView hierarchy programmatically by putting a NSStackView into a NSScroller. I have done this before and my code is correct.

The view hierarchy is what I expect until the first time through the runloop (or display) where macOS Catalina (and I think Mojave) automatically insert a NSVisualEffectView into the view hierarchy. This is messing up my custom drawing no end..

The hierarchy that I create is

NSScrollView -> NSStackView -> stack subviews

this is "enriched" by the scroll view before the first display loop into:

NSScrollView -> NSClipView -> NSStackView -> stack subviews

and after the first display the NSVisualEffects View is added at the same level as the NSClipView:

NSScrollView -> NSClipView          -> NSStackView -> stack subviews
             -> NSVisualEffectsView

Is there a way to tell the scroll view that I don't want the visual effects view?

Any help would be appreciated!


Solution

  • I'm facing the same issue when I try to recreate the spotlight window. The screenshot below shows a table view (enclosed in a scroll view) in a visual effect view:

    enter image description here

    In older version of macOS, the opaque background here is the background drawn on scroll view (or clip view), which we can simply remove it by setting a clear background color, or setting drawBackground to false as Lucas suggested.

    However, due to the insertion of this visual effect view in newer version of macOS (Catalina, maybe?), this won't works:

    enter image description here

    It has an opaque content background material, and I don't find any APIs or documents related to this behavior.


    The Workaround

    What works for me is to hide the inserted visual effect view in my NSScrollView subclass:

    class ScrollView: NSScrollView {
        override func didAddSubview(_ subview: NSView) {
            super.didAddSubview(subview)
    
            if subview is NSVisualEffectView {
                subview.isHidden = true
            }
        }
    }
    

    enter image description here


    Update

    If you are using NSTableView, instead of the above "hack", it's preferred to set table view's style to be source list:

    // for macOS 11 Big Sur
    tableView.style = .sourceList
    
    // for macOS 10.15 Catalina and older
    tableView.selectionHighlightStyle = .sourceList
    

    When you do so, the system automatically configure the scroll view and table view for source list appearance (or sidebar). More specifically, scroll view doesn't insert visual effect view, and the table view's row view are semi-transparent.