15
votes

I want to be able to add a button to the title bar of all windows that open on a Mac.

The button will go on the right hand side, opposite the X - + buttons.

This is asked about windows of my app here:
How can I create Yosemite-style unified toolbar in Interface Builder?

But I want the button to appear on all windows, of any app, that are opened on the Mac. Obviously, this will only happen once the user has installed this program.

I understand that this is essentially "plugging into" the OS's UI, but I have seen other apps do this, which makes me feel that it is do-able.

Here is screenshot where I want the button:

Screenshot

4
Just be careful where you add it- users with Mac OS X 10.7 and up will have a "Fullscreen" button on the right hand side. Make sure your button is moved to the left a bit.Sam Spencer
A useful related question: stackoverflow.com/questions/3833241/…Graham Miln

4 Answers

15
votes

The officially-supported way to add a title bar button in OS X 10.10 (Yosemite) and later is by creating an NSTitlebarAccessoryViewController and adding it to your window using -[NSWindow addTitlebarAccessoryViewController].

For example, I have a window that has a title bar accessory view:

demo window title bar

To set this up, I started by adding a standalone view to the window controller scene in my storyboard. (You don't have to use a storyboard but I did in this project.)

accessory view

My accessory view is an NSView with an NSButton subview. The button title uses Font Awesome to display the pushpin.

I connected the accessory view to an outlet (cleverly named accessoryView) in my NSWindowController subclass:

outlet connection

Then, in my window controller's windowDidLoad, I create the NSTitlebarAccessoryViewController, set its properties, and add it to the window:

@IBOutlet var accessoryView: NSView!
var accessoryViewController: NSTitlebarAccessoryViewController?

override func windowDidLoad() {
    super.windowDidLoad()
    createAccessoryViewControllerIfNeeded()
}

fileprivate func createAccessoryViewControllerIfNeeded() {
    guard self.accessoryViewController == nil else { return }
    let accessoryViewController = NSTitlebarAccessoryViewController()
    self.accessoryViewController = accessoryViewController
    accessoryViewController.view = accessoryView
    accessoryViewController.layoutAttribute = .right
    self.window?.addTitlebarAccessoryViewController(accessoryViewController)
}
12
votes

This answer addresses the latest Xcode Version 9.3

  • Go to a storyboard.
  • Drag and drop a toolbar in the storyboard to a Window.

enter image description here

  • The toolbar will be visible below a title.

enter image description here

  • Locate the Window on the storyboard

enter image description here

  • Check "Hide Title" in properties of the Window.

enter image description here

  • The toolbar is a part of the title now.

enter image description here

8
votes

This is really a two-part question. As for how to get a button up there, I’d suggest using -[NSWindow standardWindowButton:] to get an existing window button and its superview (i. e. the title bar):

NSButton *closeButton = [window standardWindowButton:NSWindowCloseButton]; // Get the existing close button of the window. Check documentation for the other window buttons.
NSView *titleBarView = closeButton.superview; // Get the view that encloses that standard window buttons.
NSButton *myButton = …; // Create custom button to be added to the title bar.
myButton.frame = …; // Set the appropriate frame for your button. Use titleBarView.bounds to determine the bounding rect of the view that encloses the standard window buttons.
[titleBarView addSubview:myButton]; // Add the custom button to the title bar.

The plugging-in is probably easiest to do as a SIMBL plug-in.

3
votes

Swift 4 - In NSWindowController add the below code

if let window = window {
    let myButton = NSButton()
    myButton.title = "Help"
    myButton.bezelStyle = .rounded

    let titleBarView = window.standardWindowButton(.closeButton)!.superview!
    titleBarView.addSubview(myButton)
    myButton.translatesAutoresizingMaskIntoConstraints = false
    titleBarView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[myButton]-2-|", options: [], metrics: nil, views: ["myButton": myButton]))
    titleBarView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-1-[myButton]-3-|", options: [], metrics: nil, views: ["myButton": myButton]))
}

Hope this is helpful.