我想在没有NSMenuItem的任何修饰符的情况下设置键等效“”(空格)(在应用程序主菜单中)。
如文件所示:
例如,在播放媒体的应用程序中,可以在没有命令键的情况下将播放命令映射到“(空格)”。您可以使用以下代码执行此操作:
[menuItem setKeyEquivalent:@“”];
[menuItem setKeyEquivalentModifierMask:0];
Key Equivalent设置成功,但不起作用。当我按下没有修饰符的“Space”键时没有任何反应,但是当我用“Fn”修饰键按下“Space”时它会起作用。
我需要使用没有修饰符的“Space”。请帮忙!
答案 0 :(得分:4)
我遇到了同样的问题。我没有非常努力地调查过,但据我所知,空格键“看起来”不像是Cocoa的键盘快捷键,所以它被路由到-insertText:
。我的解决方案是对NSWindow进行子类化,在响应链上升时捕获它(可能是你可以将NSApp子类化),并将其明确地发送到菜单系统:
- (void)insertText:(id)insertString
{
if ([insertString isEqual:@" "]) {
NSEvent *fakeEvent = [NSEvent keyEventWithType:NSKeyDown
location:[self mouseLocationOutsideOfEventStream]
modifierFlags:0
timestamp:[[NSProcessInfo processInfo] systemUptime]
windowNumber:self.windowNumber
context:[NSGraphicsContext currentContext]
characters:@" "
charactersIgnoringModifiers:@" "
isARepeat:NO
keyCode:49];
[[NSApp mainMenu] performKeyEquivalent:fakeEvent];
} else {
[super insertText:insertString];
}
}
答案 1 :(得分:1)
我刚刚遇到了同样的问题......
当我的应用中空格键等效工作正常,而NSMenuItem的链接IBAction位于App Delegate中。
如果我将IBAction移动到专用控制器中,则会失败。所有其他菜单项等效键继续工作但空格键没有响应(使用修饰键可以,但未修改的@“”将无效)。
我尝试了各种解决方法,例如直接链接到控制器与通过响应链链接,但无济于事。我尝试了代码方式:
[menuItem setKeyEquivalent:@" "];
[menuItem setKeyEquivalentModifierMask:0];
和Interface Builder方式,行为是一样的
根据Justin的回答,我已经尝试过对NSWindow进行子类化,但到目前为止还未能实现这一点。
所以现在我已经将这一个IBAction投降并重新安置到它所在的App Delegate。我不认为这是一个解决方案,只是做了...也许这是一个错误,或者(更有可能)我只是不了解事件消息和响应者链。
答案 2 :(得分:1)
这是一个棘手的问题。就像许多答案所暗示的那样,在应用程序或窗口级别拦截事件是强制菜单项起作用的可靠方法。同时,它可能会破坏其他内容,例如,如果您专注于NSTextField
或NSButton
,则希望他们使用事件,而不是菜单项。如果用户在系统偏好设置中为该菜单项重新定义了等效的键,即将Space
更改为P
,则这也可能会失败。
使用与菜单项等效的空格键的事实使事情变得更加棘手。空格键是UI特殊的事件字符之一,另外还有箭头键和其他一些字符,AppKit对此有不同的对待,在某些情况下,它们会传播到主菜单之前被消耗掉。
因此,有两件事要牢记。首先,是标准响应者链:
NSApplication.sendEvent
将事件发送到键窗口。NSWindow.sendEvent
中接收事件,确定它是否为键事件,并自行调用performKeyEquivalent
。performKeyEquivalent
将其发送到当前窗口的firstResponder
。nextResponder
。performKeyEquivalent
如果其中一个响应者使用了事件,则返回true
,否则返回false
。现在,第二个棘手的问题是,如果事件没有被使用(即performKeyEquivalent
返回false
时),window will try to process it as a special keyboard UI event – {{3中已简要提及}}:
Cocoa事件分发体系结构将某些关键事件视为命令,以将控件焦点移至窗口中的其他用户界面对象,模拟鼠标单击对象,关闭模式窗口以及在对象中进行选择允许选择。此功能称为键盘界面控制。键盘界面控件中涉及的大多数用户界面对象都是NSControl对象,但不是控件的对象也可以参与。
这部分的工作方式非常简单:
respondsToSelector
并调用它。因此,请牢记所有这些,确保两件事:
第一点很少带来麻烦。第二个,这就是您的示例中发生的情况,需要注意– AVPlayer
通常是第一个响应者,并使用空格键事件以及其他一些事件。为此,您需要覆盖keyUp
和keyDown
方法,以将事件传播到响应者链,就像在默认NSView
实现中那样。
// All player keyboard gestures are disabled.
override func keyDown(with event: NSEvent) {
self.nextResponder?.keyDown(with: event)
}
// All player keyboard gestures are disabled.
override func keyUp(with event: NSEvent) {
self.nextResponder?.keyUp(with: event)
}
以上内容将事件转发到响应者链,最终将由主菜单接收。有一个陷阱,如果第一响应者是控件,例如NSButton
或任何自定义NSControl
继承对象,它将将使用该事件。通常,您确实希望发生这种情况,但是如果不希望发生这种情况,例如在实现自定义控件时,您可以覆盖respondsToSelector
:
override func responds(to selector: Selector!) -> Bool {
if selector == #selector(performClick(_:)) { return false }
return super.responds(to: selector)
}
这将防止窗口使用键盘UI事件,因此主菜单可以接收它。但是,如果您想拦截 ALL 键盘UI事件(包括第一响应者何时能够使用它),则您确实想覆盖窗口或应用程序的performKeyEquivalent
,但不必将其复制为其他答案建议:
override func performKeyEquivalent(with event: NSEvent) -> Bool {
// Attempt to perform the key equivalent on the main menu first.
if NSApplication.shared.mainMenu?.performKeyEquivalent(with: event) == true { return true }
// Continue with the standard implementation if it doesn't succeed.
return super.performKeyEquivalent(with: event)
}
如果您在主菜单上调用performKeyEquivalent
而不检查结果,则最终可能会两次调用它–首先是手动执行,第二次是从super
实现中自动调用(如果事件未触发)被响应者链所消耗。当AVPlayer
是第一响应者,并且keyDown
和keyUp
方法没有被覆盖时,情况就是如此。
P.S。片段是Swift 4,但是想法是一样的! ✌️
答案 3 :(得分:0)
快速Swift 4-5方法:
在视图控制器中:
// Capture space and call main menu
override func keyDown(with event: NSEvent) {
if event.keyCode == 49 && !event.isARepeat{
NSApp.mainMenu?.performKeyEquivalent(with: event)
}
super.keyDown(with: event)
}