Add a custom view on the Status Bar in macOS with SwiftUI

The macOS status bar is a powerful and convenient place to display essential information or provide quick access to your app’s functionality. By default, the status bar displays a standard system icon, but sometimes you may want to create a more customized experience by using your own views. In this blog post, we’ll walk you through how to use a custom view on the status bar in macOS using Swift and SwiftUI.


Prerequisites

Before we get started, make sure you have the following:

  1. Xcode installed on your macOS machine.
  2. Basic knowledge of Swift and SwiftUI.

Creating the Project

Let’s start by creating a new macOS app project in Xcode:

  1. Open Xcode and choose “Create a new Xcode project.”
  2. Select “macOS” as the platform and choose the “App” template.
  3. Name your project and set the desired options.
  4. Click “Create” to generate the project.

Setting Up the Project

After creating the project, open the AppDelegate.swift file. This is where we’ll implement the custom status bar view. The provided code sets up a custom status bar view that shows a dummy SwiftUI view, but you can replace it with your custom view later on.


Example code

import Cocoa
import SwiftUI

@main
class AppDelegate: NSObject, NSApplicationDelegate {

    private var popover: NSPopover!
    private var statusBarItem: NSStatusItem!

    // hide the main Window when app start and create the bar UI
    func applicationDidFinishLaunching(_ aNotification: Notification) {
        NSApp.setActivationPolicy(.prohibited)
        NSApp.hide(nil)
        setupUI()
    }

    // create the bar UI
    private func setupUI() {
        // use NSHostingView to pass a SwiftUI view
        let iconView = NSHostingView(rootView: DummyView())

        // size must be defined
        iconView.frame = NSRect(x: 0, y: 0, width: 120, height: 22)
        
        statusBarItem = NSStatusBar.system.statusItem(withLength: CGFloat(NSStatusItem.variableLength))

        // add a right click action
        statusBarItem.button?.action = #selector(toggleMenu(_:))
        statusBarItem.button?.sendAction(on: [.rightMouseDown])

        statusBarItem.button?.addSubview(iconView)
        statusBarItem.button?.frame = iconView.frame
    }

    // the right-click action
    @objc func toggleMenu(_ sender: Any?) {
        let event = NSApp.currentEvent!
        if event.type == .rightMouseDown {
            statusBarItem.menu = constructMenu()
            
            let pos = NSPoint(x: statusBarItem.button!.bounds.origin.x, y: statusBarItem.button!.bounds.origin.y + statusBarItem.button!.bounds.size.height + 5)
            statusBarItem.menu!.popUp(positioning: nil, at: pos, in: statusBarItem.button)
        }
        
        statusBarItem.menu = nil
        statusBarItem.button!.action = #selector(toggleMenu(_:))
    }

    // create your right-click menu
    func constructMenu() -> NSMenu {
        let menu = NSMenu()
        menu.addItem(NSMenuItem(title: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q"))
        return menu
    }

}

Understanding the Code

Let’s go through the important parts of the code:

  1. setupUI(): This function is responsible for setting up the custom view on the status bar. It creates an instance of NSHostingView with the root SwiftUI view (DummyView() in this case) and adds it to the status bar.
  2. toggleMenu(_:): This function handles the right-click event on the status bar. When the user right-clicks on the status bar, it constructs a menu and displays it just below the custom view. The constructMenu() function creates a simple menu with a “Quit” option, which allows the user to terminate the app.
  3. constructMenu(): This function creates and returns a simple NSMenu with a “Quit” option that calls NSApplication.terminate(_:) when selected.

DummyView example content

struct DummyView: View {
    var body: some View {
        HStack {
            Text("Hello, World!")
            Image(systemName: "car")
        }
    }
}

Replacing the Dummy View

To create your own custom view on the status bar, you need to replace the DummyView() in the setupUI() function with your SwiftUI view. You can create any SwiftUI view with your desired content, style, and behavior. The size of the view should fit within the status bar’s limits.

You can play and draw any kind of small apps, for instance my Formula-1 tracker 😉 or another funny case like the Mouse Odometer:


Conclusion

Using a custom view on the macOS status bar can enhance the user experience and provide quick access to essential features of your app. With Swift and SwiftUI, implementing a custom view on the status bar becomes straightforward and flexible. By following the steps in this blog post, you should now be able to integrate your own custom view into the status bar of your macOS app. Happy coding!

 

Alberto Pasca

Software engineer @ Pirelli & C. S.p.A. with a strong passion for mobile  development, security, and connected things.