Search code examples
macosswiftuipopover

How to position NSPopover in status bar application (macOS)


I have created a macOS status bar application using SwiftUI and i finally have everything working the way i want it. The only problem is that when i use it on full screen the status bar hides and the popover menu gets chopped off. Any ideas?

How the app gets cut off

MyApp.swift:

import SwiftUI

@main
struct MyApp: App {
    @NSApplicationDelegateAdaptor(AppDelegate.self) var delegate;
    var body: some Scene {
        Settings {
            ContentView()
        }
    }
}

class AppDelegate: NSObject,NSApplicationDelegate {
    var statusItem: NSStatusItem!
    var popOver: NSPopover!
    
    func applicationDidFinishLaunching(_ notification: Notification){
        let contentView = ContentView()
        let popOver = NSPopover();
        popOver.behavior = .transient
        popOver.animates = true
        popOver.contentViewController = NSHostingController(rootView: contentView)
        popOver.setValue(true, forKeyPath: "shouldHideAnchor")
        
        self.popOver = popOver
        self.statusItem = NSStatusBar.system.statusItem(withLength: CGFloat(NSStatusItem.variableLength))
        
        if let MenuButton = self.statusItem.button {
            MenuButton.image = NSImage(systemSymbolName: "display.2", accessibilityDescription: nil)
            MenuButton.action = #selector(MenuButtonToggle)
        }
    }
    
    @objc func MenuButtonToggle(_ sender: AnyObject){
        if let button = self.statusItem.button {
            if self.popOver.isShown{
                self.popOver.performClose(sender)
            }else {
                self.popOver.show(relativeTo: button.bounds, of: button, preferredEdge: NSRectEdge.minY)
                self.popOver.contentViewController?.view.window?.makeKey()
            }
        }
    }
}

Solution

  • In your code just add a "random" larger size than the popover itself.

    I think this happens because the size of the popover is not calculated right away so there is a race condition in there, but this seems to work pretty well for me 👌

    popOver.contentSize = NSSize(width: 600, height: 1)