Drawing artefact with a NSBackgroundStyleRaised text field when the parent view draws a gradient

StackOverflow https://stackoverflow.com/questions/21519980

  •  06-10-2022
  •  | 
  •  

Question

Here is a NSView subclass that simply draws a subtle blue gradient (it's hard to see in this image but it is there) in it's bounds. Inside the view I place an NSTextField and set the cell to have NSBackgroundStyleRaised. The artefact is the background of the text field is draws with what looks like the same colour as it's superview. In this case the superview draws a gradient things look strange.

View with gradient.

The -drawRect: for the blue view is,

- (void)drawRect:(NSRect)dirtyRect
{
    [super drawRect:dirtyRect];

    // Base colours
    baseColor = [NSColor colorWithCalibratedRed: 0.271 green: 0.578 blue: 0.874 alpha: 1];
    baseColorDarkened = [NSColor colorWithCalibratedRed: 0.159 green: 0.491 blue: 0.911 alpha: 1];

    // Draw gradient
    NSGradient* gradient = [[NSGradient alloc] initWithStartingColor:baseColor  endingColor:baseColorDarkened];
    NSBezierPath* rectanglePath = [NSBezierPath bezierPathWithRect:dirtyRect];
    [gradient drawInBezierPath: rectanglePath angle: 90];

    // Draw thin border
    NSBezierPath *border = [NSBezierPath bezierPath];
    [[baseColorDarkened blendedColorWithFraction:0.2 ofColor:[NSColor blackColor]] set];
    [border setLineWidth:1];
    [border stroke];
}

I'm using a document based application so in -windowControllerDidLoadNib: I set the properties of the text field,

- (void)windowControllerDidLoadNib:(NSWindowController *)aController
{
    [super windowControllerDidLoadNib:aController];

    [[_label1 cell] setBackgroundStyle:NSBackgroundStyleLowered];
    [_label1 setBackgroundColor:[NSColor clearColor]]; // <-- Doesn't help :(
}
Was it helpful?

Solution

Some background styles seem to require special compositing (probably due to the shadows?).
OS X uses a cached & scaled representation of the superviews background when drawing the NSTextField.
I used your drawing code but changed the top color to make the cached drawing more apparent:

enter image description here

I found 2 workarounds:

  • Simply enable layer backing on the superview of the label.
  • When you can't enable layer backing, you could enforce a re-display of the label's superview. e.g. in the NSWindowDelegate method windowDidUpdate:

The second approach feels a bit hacky though:

- (void)windowDidUpdate:(NSNotification*)notification
{
   [self.label.superview setNeedsDisplay:YES];
}

Result:

enter image description here

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top