如何检测Shift键是否被按下?

33

我有一个NSView子类,希望当用户按下⇧ Shift键时它能够作出反应。然而,当前我重写的-[NSView keyDown:]方法在仅按下修饰键时不会被调用。

如何在Shift键被按下时得到通知?

7个回答

43

来自Cocoa事件处理指南:

flagsChanged: 方法可以用于检测在没有同时按下其他键的情况下按下修改键的按键操作。例如,如果用户单独按下 Option 键,则您的响应对象可以在其 flagsChanged: 实现中检测到此操作。

更多详细信息可以在这里找到。

NSResponder 的文档还说明了以下内容:

flagsChanged:

通知接收者用户已按下或释放修改键(Shift、Control 等)。

-- (void)flagsChanged:(NSEvent *)theEvent


听起来像我想要的,我会试一试。 - zneak

17

这是一个示例,稍作修改自 Matt Gemmell 的 ModKeyTest 示例应用程序。 创建一个基本的 Cocoa 应用程序,并将一个按钮与 IBAction 钩连起来,如下所示。 然后尝试使用您想要的键组合。 文档有点模糊,但 Matt 的示例非常清晰,呈现了您需要从文档中进一步利用的所有内容。

- (IBAction)myAction:(id)sender {
NSUInteger flags = [[NSApp currentEvent] modifierFlags];
if ((flags & NSCommandKeyMask) && (flags & NSAlternateKeyMask) && (flags & NSControlKeyMask)) {
    NSBeginInformationalAlertSheet(@"Modifier keys Command Option Control detected", nil, nil, nil, [NSApp mainWindow], self, nil, nil, nil,
                                   @"You sneaky thing!");
}

if ((flags & NSCommandKeyMask) && (flags & NSShiftKeyMask)) {
    NSBeginInformationalAlertSheet(@"Modifier keys Command Shift detected", nil, nil, nil, [NSApp mainWindow], self, nil, nil, nil,
                                   @"You sneaky thing!");
}

if ((flags & NSAlphaShiftKeyMask)) {
    NSBeginInformationalAlertSheet(@"Modifier keys Caps Lock detected", nil, nil, nil, [NSApp mainWindow], self, nil, nil, nil,
                                   @"You sneaky thing!");
}
if ((flags & NSFunctionKeyMask)) {
    NSBeginInformationalAlertSheet(@"Modifier keys fn detected", nil, nil, nil, [NSApp mainWindow], self, nil, nil, nil,
                                   @"You sneaky thing!");
}

我认为使用 NSApp.currentEvent 是最可靠的解决方案,特别是在混合鼠标和键盘(例如 Option-click)时。 - Ky -
这就是在这里发生的事情。 - uchuugaka
2
没错!早在2016年,我就喜欢留下评论解释为什么我点赞了帖子。 - Ky -

12

这里是处理Swift 3.0.1关键事件的代码,经过Xcode 8.2.1和macOS 10.12.2测试。

override func keyDown(with event: NSEvent) {
    var handled = false
    if event.keyCode == 53 { // ESC, same as `CMD + .`
        handled = true
        print("ESC")
    }
    if event.modifierFlags.contains(.command) { // .shift, .option, .control ...
        if let chars = event.charactersIgnoringModifiers {
            handled = true // likely we are interested with that key
            switch chars {
            case "r":
                print("CMD + r")
            case ",":
                print("CMD + ,")
            case "/":
                print("CMD + /")
            default:
                handled = false
            }
        }
    }
    if !handled {
        super.keyDown(with: event) // let system handle it(may contains sound)
    }
}

Pre-Swift 3的答案现在都已经过时了。除了NSShiftKeyMask之外,以前还有一个NSAlphaShiftKeyMask(请参见https://developer.apple.com/reference/appkit/nseventmodifierflags)-- 我正在用.shift替换前者,但后者呢?是否有Swift 3的等效物。 - wcochran
@wcochran 你好,根据苹果的源代码,在 AppKit.NSEvent.swift 文件中,第307行(Xcode 8.2.1):@available(OSX, introduced: 10.0, deprecated: 10.12, renamed: "NSEventModifierFlags.capsLock") public let NSAlphaShiftKeyMask: NSEventModifierFlags因此请使用 .capsLock - longkai

2
如果按键事件没被触发,你该怎么办?例如,当程序启动时你想测试Shift键的状态,但Shift键在程序启动前就已经按下,并且在你测试它的状态之后才会松开。
在Windows中,你可以轻松调用GetKeyState(VK_SHIFT)获取这个状态。而在macOS上,相应的方法是什么?

2
NSEvent.modifierFlags 类方法可以在任何时候返回当前修改键状态。但是我找不到一个清晰的方式来订阅这个变量的更改。 - beefon
我使用这个小帮助类...` import AppKitpublic class KeyboardHelper { public static var optionKeyIsDown: Bool { let flags = NSEvent.modifierFlags return flags.contains(.option) } public static var shiftKeyIsDown: Bool { let flags = NSEvent.modifierFlags return flags.contains(.shift) } }`所以,我只需要 KeyboardHelper.shiftKeyIsDown - Paul Stevenson

2

如果用户按下特殊键,而不管同时是否按下任何普通键,您想要查看此操作。在Swift 5中,您可以在AppDelegate中使用override func实现。

新的Swift项目中的占位符应该如下所示:

override func flagsChanged (with theEvent: NSEvent)
{
    // ...
}

现在,您可以使用以下代码填充此空函数:
override func flagsChanged (with theEvent: NSEvent)
{
    let mods: Int = (Int(theEvent.modifierFlags.rawValue)) >> 16
    let t_KeyCapsLockPressed = (bitand(mods, 1) != 0)
    let t_KeyShiftPressed = (bitand(mods, 2) != 0)
    let t_KeyControlPressed = (bitand(mods, 4) != 0)
    let t_KeyOptionPressed = (bitand(mods, 8) != 0)
    let t_KeyCommandPressed = (bitand(mods, 16) != 0)
    // let t_KeyNumericPadPressed = (bitand(mods, 32) != 0) // Not used here
    // let t_KeyHelpPressed = (bitand(mods, 64) != 0) // Not used here
    let t_KeyFnPressed = (bitand(mods, 128) != 0)
    print("flagsChanged: t_KeyShiftPressed = \(Int(t_KeyShiftPressed))")
    print("flagsChanged: t_KeyControlPressed = \(Int(t_KeyControlPressed))")
    print("flagsChanged: t_KeyOptionPressed = \(Int(t_KeyOptionPressed))")
    print("flagsChanged: t_KeyCommandPressed = \(Int(t_KeyCommandPressed))")
    // and so on...
}

每次按下或释放特殊键时都会调用该函数。因此,您可以随心所欲地进行操作。如果您将变量声明为全局变量,则可以实时从任何地方访问它们:

// In the header of your program:

var t_KeyCapsLockPressed: Bool = false
var t_KeyShiftPressed: Bool = false
var t_KeyControlPressed: Bool = false
var t_KeyOptionPressed: Bool = false
var t_KeyCommandPressed: Bool = false
var t_KeyFnPressed: Bool = false
var t_KeyFlagsHaveChanged: Bool = false

// In the AppDelegate:

override func flagsChanged (with theEvent: NSEvent)
{
    let mods: Int = (Int(theEvent.modifierFlags.rawValue)) >> 16
    t_KeyCapsLockPressed = (bitand(mods, 1) != 0)
    t_KeyShiftPressed = (bitand(mods, 2) != 0)
    t_KeyControlPressed = (bitand(mods, 4) != 0)
    t_KeyOptionPressed = (bitand(mods, 8) != 0)
    t_KeyCommandPressed = (bitand(mods, 16) != 0)
    t_KeyFnPressed = (bitand(mods, 128) != 0)
    t_KeyFlagsHaveChanged = true
}

// In your Main program:

func keyCheck ()
{
    if t_KeyFlagsHaveChanged
    {
        print("flagsChanged: t_KeyShiftPressed = \(Int(t_KeyShiftPressed))")
        print("flagsChanged: t_KeyControlPressed = \(Int(t_KeyControlPressed))")
        print("flagsChanged: t_KeyOptionPressed = \(Int(t_KeyOptionPressed))")
        print("flagsChanged: t_KeyCommandPressed = \(Int(t_KeyCommandPressed))")
    // and so on...
       t_KeyFlagsHaveChanged = false
   }
}

-3

检查是否按下:

-(void)keyDown:(NSEvent *)theEvent
{
    if ([theEvent modifierFlags] & NSShiftKeyMask)
    {
        NSLog("Shift key was pressed");
    }
}

检查发布版本:

-(void)keyUp:(NSEvent *)theEvent
{
    if(!([theEvent modifierFlags] & NSShiftKeyMask))
    {
        NSLog("Shift key was released");
    }
}

需要注意的是,只有在按下SHIFT键以及其他某个键时,才会调用NSLog函数。

希望这对你有所帮助!


恐怕不行:这正是我按照问题所做的,我需要知道而不必再按其他键。 - zneak

-4

1
我已经覆盖了NSView(从NSResponder继承)中的keyDown方法,但当我按下Shift键时该方法根本没有被调用。(我更新了我的问题以反映这一点;现在我看到它可能是混淆的一个来源。) - zneak
你有看过 键盘接口控制 这部分吗?也许即使没有同时按下其他按键,Shift键也被它截取了?如果按下 Shift + A,你能看到Shift键被按下了吗? - moxy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接