0
votes

I have a window with a custom view in it. This view get's an image drawn as a background in it. Let's assume for now that it's a structured grey background image.

- (void)drawRect:(NSRect)dirtyRect
{
    [super drawRect:dirtyRect];
    [self.titlebarBGImage drawInRect:dirtyRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1];
}

In this custom view I added a square textured button and configured it in IB to have no border. In my AppDelegate I then give this button an image in the applicationDidFinishLaunching method:

// ...
// [[self.minimizeButton cell] setBackgroundColor:[NSColor clearColor]];
[self.minimizeButton setImage:minimizeImage];
// ...

Somehow the same background (titlebarBGImage) is also drawn as the buttons background, so it has a structured grey colored image, but scaled to fit the button size. This looks silly and is not what I want obviously. If I then uncomment the line with setBackgroundColor: the background is cleared, but the custom views background also doesn't shine through where the buttons image is transparent (the button image is a circle) so I get a rectangle where the windows background shines through (i.e. green in the example image), not the background of the view the button is in.

Here's an image to make it clear what happens:

enter image description here

From top to bottom:

  • What I want to achieve, i.e. views grey structured bg shines through buttons transparent area
  • What I get with upper code, i.e. without the uncommented line: The custom views background is drawn squeezed into the buttons background
  • what I get with the clearColor line, i.e. window color (green) shines through, not the bg of the view.

How can I get the top most result?

Edit: I pushed an example project to github so that you can see what I mean.

1
I changed the title and hope someone finds this tumbleweed. I'd still need an answer, thxbeipawel

1 Answers

0
votes

One way to solve this is using Core Animation Layers. I found out watching this tutorial. For that you got to your MainMenu.xib file, select the Button object and click on the layers tab in the Utilities (see screenshot (1)). Then activate Core Animation Layer for the top most View. With that all subelements also have Core Animation Layer activated (There might be performance issues with that if the project is bigger, so take care).

How to activate Core Animation Layer

Next I subclassed the NSButton (calling it BPButtonView in my Github project which I updated now). Select the Button in the xib-file and change the Custom Class to BPButtonView, or whatever you called it.

Don't forget to set the buttons correct class name in the Utilities sidebar

The code for the new NSButton subclass looks like this:

@implementation BPButtonView

- (BOOL)wantsUpdateLayer {
    return YES;
}

- (void)updateLayer {
    if ( [self.cell isHighlighted] ) {
        self.layer.contents = [NSImage imageNamed:@"buttonbg.png"];
    } else {
        self.layer.contents = [NSImage imageNamed:@"buttonbg.png"];
    }
}

@end

I used the if-statement in the updateLayer method just to show, that depending of the buttons state (pressed vs. unpressed) you can provide a different picture. In the Video tutorial there's also a nice trick how to achieve a clean resize of a buttons picture.

And here's a screenshot of the resulting app. The red circle in the middle is the button and the green textured thing is the views background.

Resulting App with NSView background and a button placed in this view