macOS Dark Mode UI bugs with NSPredicateEditor in a Sheet

461 views Asked by At

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
3

There are 3 answers

0
pkamb On BEST ANSWER

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

}
1
pkamb On

EDIT:

This same change was introduced in an Xcode update, and is no longer needed. See the answer here: https://stackoverflow.com/a/54189665/1265393


By default, the NSPredicateEditor's grandparent view should be an NSScrollView:

Predicate Editor View Hierarchy

If you give this scroll view a clearColor transparent background, the major Dark Mode issue will be mostly fixed. If you leave this alpha change active in Light Mode, the Predicate Editor will assume a white background rather than the default grey view background.

As Apple says, make this change in layout() or one of the other methods that gives your view a chance to update itself when the system appearance changes.

class NSPredicateEditorDarkModeFix: NSPredicateEditor {

    override func layout() {
        defer { super.layout() }

        guard let clipView = self.superview as? NSClipView, let scrollView = clipView.superview as? NSScrollView else {
            return
        }

        let alpha: CGFloat = NSAppearance.current.name == .darkAqua ? 0.0 : 1.0
        scrollView.backgroundColor = scrollView.backgroundColor.withAlphaComponent(alpha)
    }

}

The Dark Mode NSPredicateEditor does still exhibit some UI issues, namely the grey background frame of each control.

Dark Mode after fix

This fix was discussed on The Omni Show podcast here:

https://theomnishow.omnigroup.com/episode/rey-worthington-omnigraffle-engineer https://twitter.com/theomnishow/status/1052630270719868928

3
pkamb On

Apple has responded to my bug report, and says that the extremely bad dark-mode behavior below was fixed by a recent update to Xcode.

You will probably still need a manual fix for the more subtle bugs in the answer here: https://stackoverflow.com/a/60842764/1265393

This answer from Apple is only a fix for the extremely bad Dark Mode bug seen here:

bad dark mode predicate editor

NSPredicateEditor presented by a Sheet is completely broken in Dark Mode

rdar://46142171

Apple Developer Relations:

To fix the dark mode appearance, you should reset that scollview background color to default

Me:

Are you saying that a fix has been made in an update to Mojave?

The scrollview clearcolor background was needed in order to make the NSPredicateEditor at all workable in Dark Mode.

Apple Developer Relations:

No, in Xcode you need to change the scollview background color to its Default (controlBackgroundColor)

Me:

Ok, I'm seeing this behave mostly ok now after switching (back) to the controlBackgroundColor. aka removing my clearColor fix

However, this was definitely NOT working in the past with that color selected. Can you provide any info about when this was fixed in OS X or Xcode?

Apple Developer Relations:

This was fixed in Xcode