macOS状态栏应用如何正确定位NSPopover避免全屏下被截断
问题描述
使用SwiftUI开发macOS状态栏应用时,全屏模式下状态栏自动隐藏,此时弹出的NSPopover菜单会被截断,效果如下:
现有核心代码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() } } } }
问题原因
全屏模式下,macOS会将当前活跃应用放在独立的专属空间中,默认创建的NSPopover会被限制在当前活跃空间的可视范围内,而隐藏的状态栏所属的菜单栏空间层级高于当前全屏应用空间,Popover无法突破当前空间的边界就会出现截断问题。
解决方法
只需修改NSPopover所属窗口的集合行为和层级,让它可以突破当前全屏空间的显示限制即可,无需重构现有逻辑,修改后的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) // 新增配置解决全屏截断问题 if let popoverWindow = self.popOver.contentViewController?.view.window { // 允许Popover跨全屏空间显示,作为全屏辅助窗口存在 popoverWindow.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary] // 设置窗口层级高于普通全屏窗口,和系统菜单层级一致 popoverWindow.level = .popUpMenu popoverWindow.makeKey() } } } }
补充说明
如果修改后仍存在边缘适配问题,可以改用自定义NSWindow代替NSPopover实现菜单,自定义窗口对层级、位置的控制灵活度更高,更适配状态栏应用的特殊显示需求。
内容的提问来源于stack exchange,提问作者tstamatakis
相关产品推荐
相关产品推荐

