【问题标题】:Swift - Capture keydown from NSViewControllerSwift - 从 NSViewController 捕获 keydown
【发布时间】:2020-02-26 08:07:17
【问题描述】:

我想在我的小应用中捕捉关键事件。

我做了什么:

class ViewController : NSViewController {
...
  override func keyDown(theEvent: NSEvent) {
        if theEvent.keyCode == 124 {
            println("abc")
        } else {
            println("abcd")
        }
    }

    override var acceptsFirstResponder: Bool {
        return true
    }

    override func becomeFirstResponder() -> Bool {
        return true
    }

    override func resignFirstResponder() -> Bool {
        return true
    }

...
}

会发生什么:

按下某个键时,会播放Funk 音效。

我看到很多帖子都在谈论这是一个代表 NSViewNSViewController 没有访问权限的代表。但是keydown 函数override auto 在NSViewController 类型的类中完成,这让我相信这是错误的。

【问题讨论】:

  • 哪个对象是第一响应者?
  • 要消除“放克声音”,只需在闭包中返回 nil 而不是事件。我使用 localMonitor 方法。
  • 以这种方式静音放克声音的一个警告是,您不会将 keyDown 事件转发到应用程序的其他部分,例如 cmd+q,为了缓解这种情况,您只能在 keyDown 事件上返回 nil想要定位,如果您想定位 a、b、c 击键,则仅在这些击键等上返回 nil。
  • 签名在 swift 4 上发生了变化
    override func keyDown(with event: NSEvent) {}

标签: macos swift


【解决方案1】:

Xcode 8.2.1 • Swift 3.0.2

import Cocoa

class ViewController: NSViewController {

    @IBOutlet var textField: NSTextField!

    override func viewDidLoad() {
        super.viewDidLoad()
        NSEvent.addLocalMonitorForEvents(matching: .flagsChanged) {
            self.flagsChanged(with: $0)
            return $0
        }
        NSEvent.addLocalMonitorForEvents(matching: .keyDown) {
            self.keyDown(with: $0)
            return $0
        }
    }
    override func keyDown(with event: NSEvent) {
        switch event.modifierFlags.intersection(.deviceIndependentFlagsMask) {
        case [.command] where event.characters == "l",
             [.command, .shift] where event.characters == "l":
            print("command-l or command-shift-l")
        default:
            break
        }
        textField.stringValue = "key = " + (event.charactersIgnoringModifiers
            ?? "")
        textField.stringValue += "\ncharacter = " + (event.characters ?? "")
    }
    override func flagsChanged(with event: NSEvent) {
        switch event.modifierFlags.intersection(.deviceIndependentFlagsMask) {
        case [.shift]:
            print("shift key is pressed")
        case [.control]:
            print("control key is pressed")
        case [.option] :
            print("option key is pressed")
        case [.command]:
            print("Command key is pressed")
        case [.control, .shift]:
            print("control-shift keys are pressed")
        case [.option, .shift]:
            print("option-shift keys are pressed")
        case [.command, .shift]:
            print("command-shift keys are pressed")
        case [.control, .option]:
            print("control-option keys are pressed")
        case [.control, .command]:
            print("control-command keys are pressed")
        case [.option, .command]:
            print("option-command keys are pressed")
        case [.shift, .control, .option]:
            print("shift-control-option keys are pressed")
        case [.shift, .control, .command]:
            print("shift-control-command keys are pressed")
        case [.control, .option, .command]:
            print("control-option-command keys are pressed")
        case [.shift, .command, .option]:
            print("shift-command-option keys are pressed")
        case [.shift, .control, .option, .command]:
            print("shift-control-option-command keys are pressed")
        default:
            print("no modifier keys are pressed")
        }
    }
}

要消除按下字符键时发出的咕噜声,您需要对视图进行子类化,重写方法 performKeyEquivalent 并返回 true。

import Cocoa

class View: NSView {
    override func performKeyEquivalent(with event: NSEvent) -> Bool {
        return true
    }
}

Sample Project

【讨论】:

  • 如何判断您使用的是 swift 1.2 还是 2.0
  • 这适用于 Swift 2.x - 我可以在我的覆盖 func keyDown() 事件代码中获取 keyDown 事件,但我仍然会收到“Funk”声音如何消除“Funk”声音?
  • Leo Dabus - 你是否建议我使用你的隐形按钮想法来消除 keyDown 事件的“Funk”声音?
  • 我的窗口(视图)有几个“标签”字段(标签实际上只是 NSTextFields)。
【解决方案2】:

Swift4

刚刚为同样的问题找到了解决方案,Swift4。这背后的想法:如果按下的键是由自定义逻辑处理的,则处理程序应返回 nil,否则(未处理的)事件...

class MyViewController: NSViewController {
   override func viewDidLoad() {
      super.viewDidLoad()
      // ...
      NSEvent.addLocalMonitorForEvents(matching: .keyDown) {
         if self.myKeyDown(with: $0) {
            return nil
         } else {
            return $0
         }
      }
   }
   func myKeyDown(with event: NSEvent) -> Bool {
      // handle keyDown only if current window has focus, i.e. is keyWindow
      guard let locWindow = self.view.window,
         NSApplication.shared.keyWindow === locWindow else { return false }
      switch Int( event.keyCode) {
      case kVK_Escape:
         // do what you want to do at "Escape"
         return true
      default: 
         return false
      }
   }
}

我们在这里:按下键时没有 Purr / Funk 声音......

[更新] 增加了对 keyWindow 的检查。没有这个,即使另一个视图/窗口包含第一响应者,也会触发 keyDown()...

【讨论】:

  • 这是帮助我摆脱咕噜声的唯一答案
  • 是否有一种简单的方法可以检测长按(按住)某个键?
【解决方案3】:

我试图找到 swift 3 的答案,这对我有用:

斯威夫特 3

import Cocoa

// We subclass an NSView

class MainView: NSView {

    // Allow view to receive keypress (remove the purr sound)
    override var acceptsFirstResponder : Bool {
        return true
    }

    // Override the NSView keydown func to read keycode of pressed key
    override func keyDown(with theEvent: NSEvent) {
        Swift.print(theEvent.keyCode)
    }

}

【讨论】:

    【解决方案4】:

    我设法从 NSWindowController 的子类中让它工作

    class MyWindowController: NSWindowController {
    
        override func keyDown(theEvent: NSEvent) {
            print("keyCode is \(theEvent.keyCode)")
        }
    }
    

    更新:

    import Cocoa
    
    protocol WindowControllerDelegate {
        func keyDown(aEvent: NSEvent)
    }
    
    class WindowController: NSWindowController {
    
        var delegate: WindowControllerDelegate?
    
        override func windowDidLoad() {
            super.windowDidLoad()
            delegate = window?.contentViewController as! ViewController
        }
        override func keyDown(theEvent: NSEvent) {
            delegate?.keyDown(theEvent)
        }
    
    }
    

    和视图控制器:

    class ViewController: NSViewController, WindowControllerDelegate {
    
        @IBOutlet weak var textField: NSTextField!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            // Do any additional setup after loading the view.
        }
    
        override var representedObject: AnyObject? {
            didSet {
            // Update the view, if already loaded.
            }
        }
        override func keyDown(theEvent: NSEvent) {
            textField.stringValue = "key = " + (theEvent.charactersIgnoringModifiers
                ?? "")
            textField.stringValue += "\ncharacter = " + (theEvent.characters ?? "")
            textField.stringValue += "\nmodifier = " + theEvent.modifierFlags.rawValue.description
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多