Dino Angelov Software Architect

Movable windows in Swift with no title bar

• 2 minute read

I recently sorted through 300+ emails of feedback for Rest, a macOS app I built some years ago. If you spend a lot of time on the computer, the app reminds you to take regular breaks.

With my newfound motivation for a v2, I got to work. Since macOS 13 (Ventura) is due to be released soon, I wanted to use the latest possible tech and SDKs to build it - making it easier to maintain going forward. So, I installed a beta of macOS 13 and Xcode 14 beta 5.

One of the things the app has is “advance notification” windows that let you know a break is approaching soon. It’s a simple window, with no titlebar and a counter and some text.

Rest v1's Advance Notification window

I had a bit of trouble figuring out the right way to subclass the window and apply my settings before the window is shown, so here’s the full code if you run into the same issues or requirements.

import Foundation
import AppKit

class AdvanceNotificationWindow: NSWindow {
    required override init(contentRect: NSRect, styleMask: NSWindow.StyleMask, backing: NSWindow.BackingStoreType, defer: Bool) {
        // Initialize through our parent class first
        super.init(contentRect: contentRect, styleMask: styleMask, backing: backing, defer: `defer`)

        // Make the window movable by dragging anywhere within the window
        self.isMovableByWindowBackground = true

        self.isOpaque = false

        // Apply the right style masks, without some of these you may lose the
        // rounded corners or other styles
        self.styleMask = [.resizable, .titled, .fullSizeContentView]

        // Since the window needs to be .titled, make it transparent
        // and hide everything we can
        self.titlebarAppearsTransparent = true
        self.titleVisibility = .hidden
        self.showsToolbarButton = false
        self.standardWindowButton(.miniaturizeButton)?.isHidden = true
        self.standardWindowButton(.closeButton)?.isHidden = true
        self.standardWindowButton(.zoomButton)?.isHidden = true
    }
}