21

After a Long search regarding the NSWindow title bar color and title color, i have found a easy drawing solution. I posting this to share my knowledge.

Muruganandham K
  • 5,271
  • 5
  • 34
  • 62
  • Link to the [NSWindow documentation](https://developer.apple.com/documentation/appkit/nswindow) in case anyone else comes here by accident wondering what it is. – Ed Randall Feb 24 '20 at 06:10

3 Answers3

27

There is a much easier way to do this. I've read that you can change a window's appearance to "textured" in a Storyboard / .xib file, but that doesn't work completely (titlebar color is mottled). However, you can change the color in your code with only two lines:

window.titlebarAppearsTransparent = true // gives it "flat" look
window.backgroundColor = <NSColor> // set the background color
Sam Claus
  • 1,807
  • 23
  • 39
  • @Tommy Yeah no problem. – Sam Claus Mar 04 '16 at 00:16
  • This is the simplest and more elegant way. Thank you. – rmvz3 Mar 19 '16 at 02:29
  • 1
    there is one flaw with this approach: when you want to present a sheet modal from that window, the sheet would descend from the top edge of the window, not from below the titlebar... – Lvsti Oct 21 '16 at 14:19
  • @Lvsti Hmmm.. I never had the need to present a sheet modal with my project, so I don't know what to do about that. I just figured I'd share what I ran into so people didn't have to smash their heads into walls copying a hundred lines of Objective-C from above. :/ – Sam Claus Jan 06 '17 at 21:58
  • This is awesome, but if I set it to a dark color, the window title is still black, and very hard to read. Any way to set the color of the window's title? – user1118321 Feb 17 '17 at 04:28
  • No idea. If you're okay with a dark theme altogether, you can set the theme of the window to the constant NSDarkVibrant or something like that. Don't remember the exact name but it's pretty straightforward. – Sam Claus Feb 17 '17 at 16:29
  • another flaw with this approach is, that the modal sheet to edit a toolbar will open directly above the toolbar that is to be edited. – deflozorngott Sep 11 '18 at 12:40
  • But this doesn't change the color of TextField of the titlebar. – Anoop Vaidya Jul 29 '20 at 18:40
  • @AnoopVaidya Yeah I’ve heard it has a couple drawbacks, it is a bit of a hack. I didn’t work much on Mac applications so I have no clue how to do it properly. Maybe you can set the color of the title text field separately? – Sam Claus Jul 29 '20 at 18:48
22

Sub class a NSView with name MyTitleView and add the following code

- (void)drawString:(NSString *)string inRect:(NSRect)rect {
    static NSDictionary *att = nil;
    if (!att) {
        NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
        [style setLineBreakMode:NSLineBreakByTruncatingTail];
        [style setAlignment:NSCenterTextAlignment];
        att = [[NSDictionary alloc] initWithObjectsAndKeys: style, NSParagraphStyleAttributeName,[NSColor whiteColor], NSForegroundColorAttributeName,[NSFont fontWithName:@"Helvetica" size:12], NSFontAttributeName, nil];
        [style release];

    }

    NSRect titlebarRect = NSMakeRect(rect.origin.x+20, rect.origin.y-4, rect.size.width, rect.size.height);


    [string drawInRect:titlebarRect withAttributes:att];
}


- (void)drawRect:(NSRect)dirtyRect
{
    NSRect windowFrame = [NSWindow  frameRectForContentRect:[[[self window] contentView] bounds] styleMask:[[self window] styleMask]];
    NSRect contentBounds = [[[self window] contentView] bounds];

    NSRect titlebarRect = NSMakeRect(0, 0, self.bounds.size.width, windowFrame.size.height - contentBounds.size.height);
    titlebarRect.origin.y = self.bounds.size.height - titlebarRect.size.height;

    NSRect topHalf, bottomHalf;
    NSDivideRect(titlebarRect, &topHalf, &bottomHalf, floor(titlebarRect.size.height / 2.0), NSMaxYEdge);

    NSBezierPath * path = [NSBezierPath bezierPathWithRoundedRect:self.bounds xRadius:4.0 yRadius:4.0];
    [[NSBezierPath bezierPathWithRect:titlebarRect] addClip];



    NSGradient * gradient1 = [[[NSGradient alloc] initWithStartingColor:[NSColor colorWithCalibratedWhite:0.0 alpha:1.0] endingColor:[NSColor colorWithCalibratedWhite:1 alpha:1.0]] autorelease];
    NSGradient * gradient2 = [[[NSGradient alloc] initWithStartingColor:[NSColor colorWithCalibratedWhite:1 alpha:1.0] endingColor:[NSColor colorWithCalibratedWhite:0 alpha:1.0]] autorelease];

  [path addClip];


//    [[NSColor colorWithCalibratedWhite:0.00 alpha:1.0] set];
//   [path fill];


   [gradient1 drawInRect:topHalf angle:270.0];
    [gradient2 drawInRect:bottomHalf angle:270.0];

    [[NSColor blackColor] set];
    NSRectFill(NSMakeRect(0, -4, self.bounds.size.width, 1.0));


    [self drawString:@"My Title" inRect:titlebarRect];


}

In appDelegate Import the class MyTitleView and add the following code

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    NSRect boundsRect = [[[_window contentView] superview] bounds];
    BlackTitlebarView * titleview = [[BlackTitlebarView alloc] initWithFrame:boundsRect];
    [titleview setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];

    [[[_window contentView] superview] addSubview:titleview positioned:NSWindowBelow relativeTo:[[[[_window contentView] superview] subviews] objectAtIndex:0]];
}
Muruganandham K
  • 5,271
  • 5
  • 34
  • 62
  • 11
    This absolutely works, but Apple does note that as of Yosemite: "New since WWDC seed: NSWindow has never supported clients adding subviews to anything other than the contentView. Some applications would add subviews to the contentView.superview (also known as the border view of the window). NSWindow will now log when it detects this scenario: "NSWindow warning: adding an unknown subview:". Applications doing this will need to fix this problem, as it prevents new features on 10.10 from working properly. See titlebarAccessoryViewControllers for official API." – Ben Flynn Jul 27 '15 at 17:24
1

No Warning message on OSX 10.10. like that

NSView *windowTopView = [[_window standardWindowButton:NSWindowCloseButton] superview];

-(void) applicationDidFinishLaunching:(NSNotification *)aNotification {

NSRect boundsRect = [windowTopView bounds];

NSView *windowTopView = [[_window standardWindowButton:NSWindowCloseButton] superview];
BTitleView * titleview = [[BTitleView alloc] initWithFrame:boundsRect];
[titleview setTitleImagePath:[[NSBundle mainBundle] pathForResource:@"Top1-01" ofType:@"bmp"]] ;
[titleview setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
[windowTopView addSubview:titleview positioned:NSWindowBelow relativeTo:[[windowTopView subviews] objectAtIndex:0]];

//addTitlebarAccessoryViewController
NSTitlebarAccessoryViewController *dummyTitlebarAccessoryViewController;
NSView * logoview = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 10, 11)];
dummyTitlebarAccessoryViewController = [NSTitlebarAccessoryViewController new];
dummyTitlebarAccessoryViewController.view = logoview;
dummyTitlebarAccessoryViewController.fullScreenMinHeight = 0;
[_window addTitlebarAccessoryViewController:dummyTitlebarAccessoryViewController];


}