Conditionally show either a Window or the Menu bar view SwiftUI macOS

656 views Asked by At

I'm creating an app where it simply lives in the menu bar, however I'd like a full-sized normal window to pop up if the user is not logged in, I have made a little pop over window which is sufficient for my main app to go into:

enter image description here

The code I have used to achieve this:

class AppDelegate: NSObject, NSApplicationDelegate{

var statusItem: NSStatusItem?
var popOver = NSPopover()

func applicationDidFinishLaunching(_ notification: Notification) {
    
    let menuView = MenuView().environmentObject(Authentication())
    
    popOver.behavior = .transient
    popOver.animates = true
    popOver.contentViewController = NSViewController()
    popOver.contentViewController?.view = NSHostingView(rootView: menuView)
    
    popOver.contentViewController?.view.window?.makeKey()
    
    statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
    
    if let MenuButton = statusItem?.button{
        
        MenuButton.image = NSImage(systemSymbolName: "gearshape.fill", accessibilityDescription: nil)
        MenuButton.action = #selector(MenuButtonToggle)
        
    }
    
    if let window = NSApplication.shared.windows.first {
        window.close()
    }
}

@objc func MenuButtonToggle(sender: AnyObject? = nil){
    
    if popOver.isShown{
        popOver.performClose(sender)
    }
    else{
        if let menuButton = statusItem?.button{
            NSApplication.shared.activate(ignoringOtherApps: true)
            self.popOver.show(relativeTo: menuButton.bounds, of: menuButton, preferredEdge: NSRectEdge.minY)
            
        }
    }
}

@objc func closePopover(_ sender: AnyObject? = nil) {
    popOver.performClose(sender)
}

@objc func togglePopover(_ sender: AnyObject? = nil) {
    if popOver.isShown {
        closePopover(sender)
    } else {
        MenuButtonToggle(sender: sender)
    }
}
}

I make the popover view inside the AppDelegate, I'd like to either render this (with the icon in the menu bar) or just a normal macOS window (without the icon in the menu bar). Then have the ability to switch between the two easily via something like this:

if session != nil{
    // show menu bar style
else{

    // show window view to log in
}
1

There are 1 answers

0
chenxi On

I think you can reference the demo

Create a reference to an instance of NSWindowController in your AppDelegate class.

private var mainVC: MainViewController?

func showMainWindow() {
    if mainVC == nil {
        mainVC = MainViewController.create()
        mainVC?.onWindowClose = { [weak self] in
            self?.mainVC = nil
        }
    }
    mainVC?.showWindow(self)
}

The MainviewController is like following:

class MainViewController: NSWindowController {

    var onWindowClose: (() -> Void)?
    
    static func create() -> MainViewController {
        let window = NSWindow()
        window.center()
        
        window.styleMask = [.titled, .closable, .miniaturizable, .resizable]
        window.title = "This is a test main title"
        
        let vc = MainViewController(window: window)
        // Use your SwiftUI here as the Main Content
        vc.contentViewController = NSHostingController(rootView: ContentView())
        return vc
    }
    
    override func showWindow(_ sender: Any?) {
        super.showWindow(sender)
        
        NSApp.activate(ignoringOtherApps: true)
        
        window?.makeKeyAndOrderFront(self)
        window?.delegate = self
    }
}

extension MainViewController: NSWindowDelegate {
    func windowWillClose(_ notification: Notification) {
        onWindowClose?()
    }
}