swift macOS菜单弹出框在启动时出现在错误的位置

envsm3lx  于 2023-06-04  发布在  Swift
关注(0)|答案(1)|浏览(117)

我有一个macOS菜单栏应用程序,我希望它在应用程序启动后显示弹出框。我遇到的问题是,虽然它确实显示了弹出框,但它将其放置在屏幕的左下角,而不是直接位于菜单栏图标下方。
有没有办法让它自动将弹出框放置在正确的位置?
下面是我的代码:

import Foundation
import Cocoa
import SwiftUI

class AppDelegate: NSObject, NSApplicationDelegate {
    var window: NSWindow!
    var statusBarItem: NSStatusItem!
    let popover = NSPopover()
    
    func applicationDidFinishLaunching(_ aNotification: Notification) {
        let statusBar = NSStatusBar.system
        let iconName = UserDefaults.standard.string(forKey: "selectedIcon") ?? "Color"
        statusBarItem = statusBar.statusItem(withLength: NSStatusItem.squareLength)
        statusBarItem.button?.image = NSImage(named: iconName)
        statusBarItem.button?.image?.size = NSSize(width: 18, height: 18)
        
        let contentView = ContentView()
        
        popover.behavior = .transient
        popover.contentViewController = NSHostingController(rootView: contentView)
        
        statusBarItem.button?.action = #selector(togglePopover(_:))
        statusBarItem.button?.target = self
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
            self.statusBarItem.button?.window?.layoutIfNeeded()
            self.statusBarItem.button?.performClick(nil)
        }
    }
    
    @objc func togglePopover(_ sender: AnyObject?) {
        if let button = self.statusBarItem.button {
            if popover.isShown {
                popover.performClose(sender)
            } else {
                popover.show(relativeTo: button.bounds, of: button, preferredEdge: NSRectEdge.minY)
            }
        }
    }
}
tf7tbtn2

tf7tbtn21#

要使其正确放置,必须在NSPopover上设置contentSize。而且,显然等待的时间比您设置的延迟稍长。例如,这起作用:

let contentSize: CGSize = .init(width: 200, height: 300) // <-- Here

@main
class AppDelegate: NSObject, NSApplicationDelegate {
    var window: NSWindow!
    var statusBarItem: NSStatusItem!
    let popover = NSPopover()
    
    func applicationDidFinishLaunching(_ aNotification: Notification) {
        let statusBar = NSStatusBar.system
        statusBarItem = statusBar.statusItem(withLength: NSStatusItem.squareLength)
        statusBarItem.button?.image = NSImage(systemSymbolName: "pencil", accessibilityDescription: nil)
        statusBarItem.button?.image?.size = NSSize(width: 18, height: 18)
        
        let contentView = ContentView()
        
        popover.behavior = .transient
        popover.contentViewController = NSHostingController(rootView: contentView)
        popover.contentSize = contentSize // <-- Here
        
        statusBarItem.menu = nil
        statusBarItem.button?.action = #selector(togglePopover(_:))
        statusBarItem.button?.target = self
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { // <-- Here
            self.statusBarItem.button?.window?.layoutIfNeeded()
            self.statusBarItem.button?.performClick(nil)
        }
    }
    
    @objc func togglePopover(_ sender: NSStatusBarButton?) {
        if let button = self.statusBarItem.button {
            if popover.isShown {
                popover.performClose(sender)
            } else {
                popover.show(relativeTo: sender!.bounds, of: button, preferredEdge: NSRectEdge.minY)
            }
        }
    }
}

struct ContentView: View {
    var body: some View {
        Text("Hello, world")
            .frame(width: contentSize.width, height: contentSize.height) // <-- Here
    }
}

您还可以动态读取SwiftUI视图的大小,并基于此设置内容大小。例如https://www.fivestars.blog/articles/swiftui-share-layout-information/

相关问题