Search code examples
macosmacos-mojavenspredicateeditormacos-darkmode

macOS Dark Mode UI bugs with NSPredicateEditor in a Sheet


In a Mac app, I am presenting a sheet containing an NSPredicateEditor:

parentViewController.presentAsSheet(predicateEditor)

I created a sample project for this behavior here:
https://github.com/pkamb/Feedback_NSPredicateEditor

In macOS 10.14 Mojave and 10.15 Catalina, after the introduction of Dark Mode, this results in a number of UI bugs.

The background of the NSPredicateEditor controls does not match the background of the row / superview's background. Note the is background and the background of the text fields.

  1. Dark Mode:

Dark Mode

  1. Light Mode:

Light Mode

How can these sheet-presented NSPredicateEditor UI bugs be fixed?

Bug Reports:

  • rdar://42789149 - NSPredicateEditor does not behave well in dark mode in a sheet
  • rdar://46142171 - NSPredicateEditor presented by a Sheet is completely broken in Dark Mode

Solution

  • This answer fixes the subtly wrong background colors of NSPredicate editor controls as shown here:

    Insert a Visual Effects View using a flipped coordinate system into the Predicate Editor view hierarchy. The flipped VEV should exist between the NSClipView and the NSPredicateEditor.

    Then add autolayout constraints as shown below. Do not constrain the bottom or height anchors of the VEV.


    Note:

    • Setting the scrollView.documentView = flippedVEV fixes scrolling.

    • If you do not use an isFlipped VEV, the Predicate Editor rows stack at the bottom rather than the top.


    class FlippedVEV: NSVisualEffectView {
        override var isFlipped: Bool { return true }
    }
    
    class PredicateEditorViewController: NSViewController {
    
        @IBOutlet weak var predicateEditor: NSPredicateEditor!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            if let clipView = predicateEditor.superview as? NSClipView, let scrollView = clipView.superview as? NSScrollView {
                let flippedVEV = FlippedVEV(frame: predicateEditor.frame)
                flippedVEV.material = .sheet
    
                predicateEditor.removeFromSuperview()
                flippedVEV.addSubview(predicateEditor)
                clipView.addSubview(flippedVEV)
                scrollView.documentView = flippedVEV
    
                flippedVEV.translatesAutoresizingMaskIntoConstraints = false
                NSLayoutConstraint.activate([
                    flippedVEV.leadingAnchor .constraint(equalTo:predicateEditor.leadingAnchor ),
                    flippedVEV.trailingAnchor.constraint(equalTo:predicateEditor.trailingAnchor),
                    flippedVEV.topAnchor     .constraint(equalTo:predicateEditor.topAnchor     ),
                    flippedVEV.bottomAnchor  .constraint(equalTo:predicateEditor.bottomAnchor  ),
                    clipView.leadingAnchor   .constraint(equalTo:flippedVEV     .leadingAnchor ),
                    clipView.trailingAnchor  .constraint(equalTo:flippedVEV     .trailingAnchor),
                    clipView.topAnchor       .constraint(equalTo:flippedVEV     .topAnchor     ),
                ])
            }
        }
    
    }