So, what I want to share today is how to make windows with cool UI in a macOS application written in Cocoa/Swift.
This an example of what you see when create a new project from scratch in XCode and run it:
If you like this windows… ok, close this page and ignore the rest.
Personally I don’t like so much, I prefer to customize a little bit also if my app is very simple.
For instance, we can remove the Title bar, we can change the background color, we can drag clicking everywhere, we can also add a toolbar and much more…
This is what I want to achieve in this post:
A NSWindow without title and titlebar, with a full background color and a toolbar that start on the right.
Better than the first one, no?
Let’s go!
The first thing to do is to create some Swift Extensions because NSWindowController and NSViewController don’t have the backgroundColor method for instance.
Other extensions that we can create are useful things to reuse in our projects, like NSWindowController styles and addons.
NSCOLOR EXTENSION
just because if useful…
// NSColorExtension.swift extension NSColor { static var defaultBackground: NSColor { get { return NSColor.init( red: 83/255, green: 87/255, blue: 96/255, alpha: 1) } } }
NSVIEW BACKGROUND COLOR
// NSViewExtension.swift extension NSView { var backgroundColor: NSColor? { get { if let colorRef = self.layer?.backgroundColor { return NSColor(cgColor: colorRef) } else { return nil } } set { self.wantsLayer = true self.layer?.backgroundColor = newValue?.cgColor } } }
NSVIEWCONTROLLER extension
// NSViewControllerExtension.swift extension NSViewController { func darkBackground() { self.view.backgroundColor = NSColor.defaultBackground } }
and the latest one is for NSWindowController,
NSWINDOWCONTROLLEr styles
//NSWindowControllerExtension.swift extension NSWindowController { func smartWindow() { self.window?.styleMask.insert( NSWindow.StyleMask.unifiedTitleAndToolbar) self.window?.styleMask.insert( NSWindow.StyleMask.fullSizeContentView) self.window?.styleMask.insert(NSWindow.StyleMask.titled) self.window?.toolbar?.isVisible = false self.window?.titleVisibility = .hidden self.window?.titlebarAppearsTransparent = true } func activateWindowDrag() { self.window?.isMovableByWindowBackground = true } }
This last extension do the biggest cool part!
Let’s see how to “colorize” this window and next how to add the toolbar on the right:
- Create a new file, new Cocoa class, subclass of NSWindowController and add to project. Call it “MainWindow.swift”
- Open Main.storyboard and set the class type to the Window to your class name that just created, “MainWindow“.
- Open MainWindow.swift and:
// create an instance of the ViewController: var vctrl : ViewController? override func windowDidLoad() { super.windowDidLoad() // and assign to the contentViewController vctrl = self.contentViewController as? ViewController } }
Now your storyboard start with MainWindow and load the ViewController as usual.
Add cool things now!
in the windowDidLoad(), add the two extension method that you’ve created before:
// smart window, add the cool style that you want: // - hide the title bar // - set the window in full screen // - unified the title with the window // - set visibility of elements self.smartWindow() // activateWindowDrag is another cool thing that permit you to drag the window clicking anywhere! self.activateWindowDrag()
Build and run!
Better, but not yet complete!
We want now to change the background color of this window… you can’t. Ah no, wait, we created an extension for doing this!
Let’s use it!
Open ViewController.swift and in the
override func viewDidLoad() { super.viewDidLoad() // yeah! Invoke the dark background method from extension! self.darkBackground() }
Build, run and voilà! another step is done!
Very nice!
Now, the last thing, add the toolbar!
Open the storyboard and create a new NSViewController dragging it to the storyboard. Select it and change the class to “NSTitlebarAccessoryViewController” and set a “storyboardId” in the Identity tab like “titlebarViewController“, just to call from code.
You should create something like this:
You can add buttons, images and what you want to customize this accessory view (like my second screenshot, with Info button and Settings button).
Let’s come back in the code, in MainWindow.swift. You need to set the accessory view of the window with the new controller just created:
self.window?.addTitlebarAccessoryViewController
Exactly in the windowDidLoad(). This is your final result:
override func windowDidLoad() { super.windowDidLoad() self.smartWindow() self.activateWindowDrag() if let titlebarController = self.storyboard?.instantiateController( withIdentifier: NSStoryboard.SceneIdentifier( rawValue: "titlebarViewController") ) as? NSTitlebarAccessoryViewController { // position of title bar titlebarController.layoutAttribute = .right // set the titleBar self.window?.addTitlebarAccessoryViewController( titlebarController ) } vctrl = self.contentViewController as? ViewController }
For the last time, build and run! You should see your application complete!
You can create your own class for the accessory view and attach the button’s action on it!
Lazy? Download source code on github: https://github.com/elpsk/Swift-macOS-CoolApp-Skeleton
Done!