SwiftUI 使用键盘快捷键调用 NSPopover
Posted
技术标签:
【中文标题】SwiftUI 使用键盘快捷键调用 NSPopover【英文标题】:SwiftUI Invoke NSPopover with Keyboard Shortcut 【发布时间】:2021-07-03 19:09:31 【问题描述】:我正在使用 SwiftUI for macOS Big Sur 构建一个菜单栏应用程序,但不知道如何使用键盘快捷键打开弹出框(应用程序的主窗口,因为它是一个菜单栏应用程序)。我希望用户能够通过按 Command + [a letter] 查看窗口,无论他们在计算机上做什么(当然,只要应用程序处于打开状态)。以下是控制popover的主要函数和代码:
@main
struct MenuBarPopoverApp: App
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene
Settings
EmptyView()
.commands
MenuBarPopoverCommands(appDelegate: appDelegate)
class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject
var popover = NSPopover.init()
var statusBarItem: NSStatusItem?
var contentView: ContentView!
override class func awakeFromNib()
func applicationDidFinishLaunching(_ notification: Notification)
print("Application launched")
NSApplication.shared.activate(ignoringOtherApps: true)
contentView = ContentView()
popover.animates = false
popover.behavior = .transient
let contentVc = NSViewController()
contentVc.view = NSHostingView(rootView: contentView.environmentObject(self))
popover.contentViewController = contentVc
statusBarItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
let itemImage = NSImage(named: "statusBarIcon")
itemImage?.isTemplate = true
statusBarItem?.button?.image = itemImage
statusBarItem?.button?.action = #selector(AppDelegate.togglePopover(_:))
@objc func showPopover(_ sender: AnyObject?)
if let button = statusBarItem?.button
NSApplication.shared.activate(ignoringOtherApps: true)
popover.show(relativeTo: button.bounds, of: button, preferredEdge: NSRectEdge.minY)
@objc func closePopover(_ sender: AnyObject?)
popover.performClose(sender)
@objc func togglePopover(_ sender: AnyObject?)
if popover.isShown
closePopover(sender)
else
showPopover(sender)
还有 MenuBarPopoverCommands(应用程序的主要部分是一个文本编辑器,所以我有一堆与之相关的键盘快捷键):
struct MenuBarPopoverCommands: Commands
let appDelegate: AppDelegate
init(appDelegate: AppDelegate)
self.appDelegate = appDelegate
var body: some Commands
CommandMenu("Edit")
Section
Button("Cut")
appDelegate.contentView.editCut()
.keyboardShortcut(KeyEquivalent("x"), modifiers: .command)
Button("Copy")
appDelegate.contentView.editCopy()
.keyboardShortcut(KeyEquivalent("c"), modifiers: .command)
Button("Paste")
appDelegate.contentView.editPaste()
.keyboardShortcut(KeyEquivalent("v"), modifiers: .command)
Button("Undo")
appDelegate.contentView.undo()
.keyboardShortcut(KeyEquivalent("z"), modifiers: .command)
Button("Redo")
appDelegate.contentView.redo()
.keyboardShortcut(KeyEquivalent("z"), modifiers: [.command, .shift])
Button("Bold")
appDelegate.contentView.bold()
.keyboardShortcut(KeyEquivalent("b"), modifiers: .command)
Button("Italic")
appDelegate.contentView.italic()
.keyboardShortcut(KeyEquivalent("i"), modifiers: .command)
Button("Select All")
appDelegate.contentView.editSelectAll()
.keyboardShortcut(KeyEquivalent("a"), modifiers: .command)
【问题讨论】:
【参考方案1】:Swift 5 解决方案在https://***.com/a/58225397/3984522 中提出。但是,有一个不错的包,只需几行代码即可完成https://github.com/soffes/HotKey 的工作:
import SwiftUI
import HotKey
@main
struct MenuBarPopoverApp: App
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene
Settings
EmptyView()
.commands
MenuBarPopoverCommands(appDelegate: appDelegate)
class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject
var popover = NSPopover.init()
var statusBarItem: NSStatusItem?
var contentView: ContentView!
let hotKey = HotKey(key: .x, modifiers: [.control, .shift]) // Global hotkey
override class func awakeFromNib()
func applicationDidFinishLaunching(_ notification: Notification)
print("Application launched")
NSApplication.shared.activate(ignoringOtherApps: true)
contentView = ContentView()
popover.animates = false
popover.behavior = .transient
let contentVc = NSViewController()
contentVc.view = NSHostingView(rootView: contentView.environmentObject(self))
popover.contentViewController = contentVc
statusBarItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
let itemImage = NSImage(systemSymbolName: "eye", accessibilityDescription: "eye")
itemImage?.isTemplate = true
statusBarItem?.button?.image = itemImage
statusBarItem?.button?.action = #selector(AppDelegate.togglePopover(_:))
hotKey.keyUpHandler = // Global hotkey handler
self.togglePopover()
@objc func showPopover(_ sender: AnyObject? = nil)
if let button = statusBarItem?.button
NSApplication.shared.activate(ignoringOtherApps: true)
popover.show(relativeTo: button.bounds, of: button, preferredEdge: NSRectEdge.minY)
@objc func closePopover(_ sender: AnyObject? = nil)
popover.performClose(sender)
@objc func togglePopover(_ sender: AnyObject? = nil)
if popover.isShown
closePopover(sender)
else
showPopover(sender)
【讨论】:
我尝试在我的代码中实现有关 HotKey 的行,但在此行上出现错误:hotkey.keyUpHandler = self.statusBar?.togglePopover()
。错误是:“AppDelegate”类型的值没有成员“statusBar”。我的代码中的“statusBar”是什么?
嗨@RohanTaneja,我已经更新了解决方案并从中删除了控制器。所以现在代码几乎和你的一样。您在appDelegate
上实现了statusBar
,因此您可以只调用appDelegate.togglePopover
或从委托本身内部调用self.togglePopover
。以上是关于SwiftUI 使用键盘快捷键调用 NSPopover的主要内容,如果未能解决你的问题,请参考以下文章
使用 SwiftUI 的 Catalyst 应用程序中的键盘命令连续触发