Showing SwiftUI Below NSStatusItem When Button is Clicked on Over SwiftUI View

I see a lot of tutorials that show how to open a SwiftUI View when a NSStatusItem is clicked on. That's not what I want. I need to show a SwiftUI View when I click on a button over SwiftUI View. So far the following is what I have.

import SwiftUI

@main
struct MyStatusApp_App: App {
    @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

#if os(macOS)
class AppDelegate: NSObject, NSApplicationDelegate {
    var statusItem: NSStatusItem!
    private var popover: NSPopover?

    func applicationDidFinishLaunching(_ notification: Notification) {
        hideTitleBar()
        
        NSApp.setActivationPolicy(.accessory)
        
        statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
        if let button = statusItem.button {
            if let image = NSImage(named: "statusImage") {
                button.image = image
        }
    }
}
#endif

// ContentView //
import SwiftUI

struct ContentView: View {
    @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    var body: some View {
        VStack {
            Button("Click me!") {
                let popOver = NSPopover()
                popOver.contentViewController = NSHostingController(rootView: NotificationView())
                appDelegate.statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
                if let statusBarbutton = appDelegate.statusItem.button {
                    popOver.show(relativeTo: statusBarbutton.bounds, of: statusBarbutton, preferredEdge: .minY)
                }
            }
        }
        .frame(width: 200, height: 100)
    }
}

If I run the application and click on the button (orange arrow) over ContentView, a guy from NotificationView will appear (green rectangle).  That's good.  But it appears not below the status item (red arrow).  It's positioned at an odd location.  It's way below the status item guy.  What am I doing wrong?  Muchos thankos.

I guess site's add image function is broken.  It doesn't show my screenshot.

![]("https://developer.apple.com/forums/content/attachment/7e19bf2e-439d-4ed0-a03c-740b77e94e24" "title=Screenshot.jpg;width=364;height=400")
Answered by Tomato in 800176022

I've gotten the following lines of code working.

import SwiftUI

@main
struct MyStatusApp_App: App {
#if os(macOS)
    @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
#endif
    @StateObject private var statusBarViewModel = StatusBarViewModel()
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .onDisappear {
                    NSApplication.shared.terminate(self)
                }
                .environmentObject(statusBarViewModel)
        }
        .defaultSize(width: 640, height: 360)
    }
}

#if os(macOS)
class AppDelegate: NSObject, NSApplicationDelegate {
    let fileManager = FileManager.default

    func applicationDidFinishLaunching(_ notification: Notification) {
        hideTitleBar()
        
        NSApp.setActivationPolicy(.accessory)
    }
    
    func applicationWillFinishLaunching(_ notification: Notification) {
        NSWindow.allowsAutomaticWindowTabbing = false
    }
}
#endif

// ContentView.swift //
import SwiftUI

struct ContentView: View {
    @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    @EnvironmentObject var statusBarViewModel: StatusBarViewModel
    
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            
            Button("Click me!") {
                if let statusItem = statusBarViewModel.statusItem {
                    if let button = statusItem.button {
                        if statusBarViewModel.popover == nil {
                            let popover = NSPopover()
                            statusBarViewModel.popover = popover
                            popover.behavior = .transient
                            popover.animates = false
                            popover.contentViewController = NSHostingController(rootView: NotificationView())
                        }
                        statusBarViewModel.popover?.show(relativeTo: button.bounds, of: button, preferredEdge: .minY)
                    }
                }
            }
        }
        .frame(width: 200, height: 100)
        .onAppear {
            statusBarViewModel.makeStatusMenu()
        }
    }
}

class StatusBarViewModel: ObservableObject {
    @Published var statusItem: NSStatusItem!
    @Published var popover: NSPopover?
    
    func makeStatusMenu() {
        statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
        if let button = statusItem.button {
            if let image = NSImage(named: "statusImage") {
                button.image = image
            }
        }
    }
}
Accepted Answer

I've gotten the following lines of code working.

import SwiftUI

@main
struct MyStatusApp_App: App {
#if os(macOS)
    @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
#endif
    @StateObject private var statusBarViewModel = StatusBarViewModel()
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .onDisappear {
                    NSApplication.shared.terminate(self)
                }
                .environmentObject(statusBarViewModel)
        }
        .defaultSize(width: 640, height: 360)
    }
}

#if os(macOS)
class AppDelegate: NSObject, NSApplicationDelegate {
    let fileManager = FileManager.default

    func applicationDidFinishLaunching(_ notification: Notification) {
        hideTitleBar()
        
        NSApp.setActivationPolicy(.accessory)
    }
    
    func applicationWillFinishLaunching(_ notification: Notification) {
        NSWindow.allowsAutomaticWindowTabbing = false
    }
}
#endif

// ContentView.swift //
import SwiftUI

struct ContentView: View {
    @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    @EnvironmentObject var statusBarViewModel: StatusBarViewModel
    
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            
            Button("Click me!") {
                if let statusItem = statusBarViewModel.statusItem {
                    if let button = statusItem.button {
                        if statusBarViewModel.popover == nil {
                            let popover = NSPopover()
                            statusBarViewModel.popover = popover
                            popover.behavior = .transient
                            popover.animates = false
                            popover.contentViewController = NSHostingController(rootView: NotificationView())
                        }
                        statusBarViewModel.popover?.show(relativeTo: button.bounds, of: button, preferredEdge: .minY)
                    }
                }
            }
        }
        .frame(width: 200, height: 100)
        .onAppear {
            statusBarViewModel.makeStatusMenu()
        }
    }
}

class StatusBarViewModel: ObservableObject {
    @Published var statusItem: NSStatusItem!
    @Published var popover: NSPopover?
    
    func makeStatusMenu() {
        statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
        if let button = statusItem.button {
            if let image = NSImage(named: "statusImage") {
                button.image = image
            }
        }
    }
}
Showing SwiftUI Below NSStatusItem When Button is Clicked on Over SwiftUI View
 
 
Q