Handling left and right click at NSStatusBar with Swift 3

Swift 3 and Xcode 8 are out, so it's time to convert my codebase to the most recent Swift version.

I'm working on a macOS (I'm still getting used to the new OS X name) application that has not been released yet. So, I decided to spend a day for migrating its codebase to Swift 3.

My project is a status bar application that displays a popover view when the status bar icon is left-clicked. It also displays a "standard" menu when right-clicked.

Here is my updated Swift 3 code:

let kStatusIcon = "statusIcon"

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {  
    @IBOutlet weak var contextMenu: NSMenu!

    let popover = NSPopover()    
    let statusItem = NSStatusBar.system().statusItem(withLength: NSVariableStatusItemLength)

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        if let button = statusItem.button {
            button.image = NSImage(named: kStatusIcon)
            button.target = self
            button.action = #selector(self.statusBarButtonClicked(sender:))
            button.sendAction(on: [.leftMouseUp, .rightMouseUp])

            let storyboard = NSStoryboard(name: "Main", bundle: nil)
            popover.contentViewController = storyboard.instantiateController(withIdentifier: "PopoverViewController") as! PopoverViewController
        }
    }

    func statusBarButtonClicked(sender: NSStatusBarButton) {
        let event = NSApp.currentEvent!

        if event.type == NSEventType.rightMouseUp {
            closePopover(sender: nil)

            statusItem.menu = contextMenu
            statusItem.popUpMenu(contextMenu)

            // This is critical, otherwise clicks won't be processed again
            statusItem.menu = nil
        } else {
            togglePopover(sender: nil)
        }
    }

    func togglePopover(sender: AnyObject?) {
        if popover.isShown {
            closePopover(sender: sender)
        } else {
            showPopover(sender: sender)
        }
    }

    func showPopover(sender: AnyObject?) {
        if let button = statusItem.button {
            popover.show(relativeTo: button.bounds, of: button, preferredEdge: NSRectEdge.minY)
        }
    }

    func closePopover(sender: AnyObject?) {
        popover.performClose(sender)
    }
}

I really like the updated Swift syntax and Cocoa library. It's definitely more readable and human-friendly. I'm loving its shorter method names.

Michael Samoylov

Python, JavaScript and Swift Expert with 12+ years of experience.

Vilnius, Lithuania https://monmar.tech

Subscribe to Michael Samoylov

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!