【问题标题】:Cocoa Keyboard Shortcuts in Dialog without an Edit Menu没有编辑菜单的对话框中的 Cocoa 键盘快捷键
【发布时间】:2010-11-01 12:36:54
【问题描述】:

我有一个显示菜单栏状态项的LSUIElement 应用程序。应用程序可以显示一个包含文本字段的对话窗口。

如果用户右键单击/控制单击文本字段,则会出现一个菜单,允许剪切、复制、粘贴等。但是,标准的 Command-X、Command-C 和 Command-V 键盘快捷键不在外地工作。我认为这是因为我的应用程序没有提供定义了这些快捷方式的编辑菜单。

按照Ship Some Code 博客中的建议,我已尝试将“编辑”菜单项添加到我的应用程序菜单中,但没有奏效。编辑菜单中的菜单项可以使用,但键盘快捷键仍然不起作用。

我可以想象一些破解键盘处理的方法,但有没有“推荐”的方法来完成这项工作?

(有关应用程序的详细信息,请参阅Menubar Countdown。)

相关问题:Copy/Paste Not Working in Modal Window

【问题讨论】:

    标签: cocoa macos nsstatusitem


    【解决方案1】:

    改进 CocoaRocket 解决方案:

    以下内容可以省去子类 NSTextField 并记住在整个应用程序中使用子类;它还将为处理它们的其他响应者启用复制、粘贴和朋友,例如。 NSTextView。

    将它放在 NSApplication 的子类中,并相应地更改 Info.plist 中的主体类。

    - (void) sendEvent:(NSEvent *)event {
        if ([event type] == NSKeyDown) {
            if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) == NSCommandKeyMask) {
                if ([[event charactersIgnoringModifiers] isEqualToString:@"x"]) {
                    if ([self sendAction:@selector(cut:) to:nil from:self])
                        return;
                }
                else if ([[event charactersIgnoringModifiers] isEqualToString:@"c"]) {
                    if ([self sendAction:@selector(copy:) to:nil from:self])
                        return;
                }
                else if ([[event charactersIgnoringModifiers] isEqualToString:@"v"]) {
                    if ([self sendAction:@selector(paste:) to:nil from:self])
                        return;
                }
                else if ([[event charactersIgnoringModifiers] isEqualToString:@"z"]) {
                    if ([self sendAction:@selector(undo:) to:nil from:self])
                        return;
                }
                else if ([[event charactersIgnoringModifiers] isEqualToString:@"a"]) {
                    if ([self sendAction:@selector(selectAll:) to:nil from:self])
                        return;
                }
            }
            else if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) == (NSCommandKeyMask | NSShiftKeyMask)) {
                if ([[event charactersIgnoringModifiers] isEqualToString:@"Z"]) {
                    if ([self sendAction:@selector(redo:) to:nil from:self])
                        return;
                }
            }
        }
        [super sendEvent:event];
    }
    
    // Blank Selectors to silence Xcode warnings: 'Undeclared selector undo:/redo:'
    - (IBAction)undo:(id)sender {}
    - (IBAction)redo:(id)sender {}
    

    【讨论】:

    • 完美解决方案!原始版本的另一个缺点是您还必须“重新定义”NSTextField 的子类,例如 NSSearchField
    • 我已对此进行了修改以包括重做。
    • 此外,我必须将NSApplication 替换为main.m 中的子类
    • 虽然我不喜欢继承 NSApplication 的想法,但这段代码运行良好。我的应用没有 xib 和主菜单,每当我尝试将 NSTextField 作为子视图添加到无边框窗口时,所有 cmd 快捷方式都不起作用(我什至覆盖了 canBecomeKeyWindow)。
    • 谢谢阿德里安,它就像一个魅力,我只是想注意我的 Xcode 发出 2 个警告,选择器 (undo:) 和选择器 (redo:) 无法识别,但它们实际上可以工作运行时,只需忽略警告。我认为我的通知可能会对某人有所帮助
    【解决方案2】:

    对我有用的是使用 CocoaRocket 的 Copy and Paste Keyboard Shortcuts 中介绍的 View 解决方案

    基本上,这意味着继承 NSTextField 并覆盖 performKeyEquivalent:

    更新: CocoaRocket 网站显然已经消失了。这是 Internet 存档链接:http://web.archive.org/web/20100126000339/http://www.cocoarocket.com/articles/copypaste.html

    编辑:Swift 代码如下所示

    class Editing: NSTextField {
    
      private let commandKey = NSEventModifierFlags.CommandKeyMask.rawValue
      private let commandShiftKey = NSEventModifierFlags.CommandKeyMask.rawValue | NSEventModifierFlags.ShiftKeyMask.rawValue
      override func performKeyEquivalent(event: NSEvent) -> Bool {
        if event.type == NSEventType.KeyDown {
          if (event.modifierFlags.rawValue & NSEventModifierFlags.DeviceIndependentModifierFlagsMask.rawValue) == commandKey {
            switch event.charactersIgnoringModifiers! {
            case "x":
              if NSApp.sendAction(Selector("cut:"), to:nil, from:self) { return true }
            case "c":
              if NSApp.sendAction(Selector("copy:"), to:nil, from:self) { return true }
            case "v":
              if NSApp.sendAction(Selector("paste:"), to:nil, from:self) { return true }
            case "z":
              if NSApp.sendAction(Selector("undo:"), to:nil, from:self) { return true }
            case "a":
              if NSApp.sendAction(Selector("selectAll:"), to:nil, from:self) { return true }
            default:
              break
            }
          }
          else if (event.modifierFlags.rawValue & NSEventModifierFlags.DeviceIndependentModifierFlagsMask.rawValue) == commandShiftKey {
            if event.charactersIgnoringModifiers == "Z" {
              if NSApp.sendAction(Selector("redo:"), to:nil, from:self) { return true }
            }
          }
        }
        return super.performKeyEquivalent(event)
      }
    }
    

    编辑: Swift 3 代码如下所示

    class Editing: NSTextView {
    
    private let commandKey = NSEventModifierFlags.command.rawValue
    private let commandShiftKey = NSEventModifierFlags.command.rawValue | NSEventModifierFlags.shift.rawValue
    
    override func performKeyEquivalent(with event: NSEvent) -> Bool {
        if event.type == NSEventType.keyDown {
            if (event.modifierFlags.rawValue & NSEventModifierFlags.deviceIndependentFlagsMask.rawValue) == commandKey {
                switch event.charactersIgnoringModifiers! {
                case "x":
                    if NSApp.sendAction(#selector(NSText.cut(_:)), to:nil, from:self) { return true }
                case "c":
                    if NSApp.sendAction(#selector(NSText.copy(_:)), to:nil, from:self) { return true }
                case "v":
                    if NSApp.sendAction(#selector(NSText.paste(_:)), to:nil, from:self) { return true }
                case "z":
                    if NSApp.sendAction(Selector(("undo:")), to:nil, from:self) { return true }
                case "a":
                    if NSApp.sendAction(#selector(NSResponder.selectAll(_:)), to:nil, from:self) { return true }
                default:
                    break
                }
            }
            else if (event.modifierFlags.rawValue & NSEventModifierFlags.deviceIndependentFlagsMask.rawValue) == commandShiftKey {
                if event.charactersIgnoringModifiers == "Z" {
                    if NSApp.sendAction(Selector(("redo:")), to:nil, from:self) { return true }
                }
            }
        }
        return super.performKeyEquivalent(with: event)
     }
    }
    

    【讨论】:

    • 回答了一个 Swift 2.2 解决方案here
    • 但这不适用于其他语言:(只需使用event.keyCode而不是event.charactersIgnoringModifiers
    • 这在 Caps Lock 开启时不起作用。在将相等性与命令或命令转换进行比较之前,您必须删除 Caps Lock 标志。
    【解决方案3】:

    我遇到了和你一样的问题,我想我已经找到了一个更简单的解决方案。您只需将原始主菜单保留在 MainMenu.xib 中 - 它不会显示,但所有操作都会得到正确处理。诀窍是它必须是原始的,如果你只是从库中拖动一个新的 NSMenu,应用程序不会将其识别为主菜单,我不知道如何将其标记为主菜单(如果你取消选中 LSUIElement,你会看到它不会出现在顶部,如果它不是原始的)。如果您已经删除了它,您可以创建一个新的示例应用程序并从其 NIB 中拖动一个菜单,这也可以。

    【讨论】:

    • 这在 2017 年对我有用。我从“主菜单”Xcode 模板创建了一个新的 xib - 不需要新的应用程序。
    • 谢谢!这是一个简单易行的解决方案。
    • 这应该是公认的答案 - 无需添加大量代码并且它是可扩展的。从示例应用程序复制/粘贴主菜单的技巧很有效(Xcode 10.1)。
    【解决方案4】:

    我改进了 Adrian 的解决方案,以便在 Caps Lock 开启时也能正常工作:

    - (void)sendEvent:(NSEvent *)event
    {
        if (event.type == NSKeyDown)
        {
            NSString *inputKey = [event.charactersIgnoringModifiers lowercaseString];
            if ((event.modifierFlags & NSDeviceIndependentModifierFlagsMask) == NSCommandKeyMask ||
                (event.modifierFlags & NSDeviceIndependentModifierFlagsMask) == (NSCommandKeyMask | NSAlphaShiftKeyMask))
            {
                if ([inputKey isEqualToString:@"x"])
                {
                    if ([self sendAction:@selector(cut:) to:nil from:self])
                        return;
                }
                else if ([inputKey isEqualToString:@"c"])
                {
                    if ([self sendAction:@selector(copy:) to:nil from:self])
                        return;
                }
                else if ([inputKey isEqualToString:@"v"])
                {
                    if ([self sendAction:@selector(paste:) to:nil from:self])
                        return;
                }
                else if ([inputKey isEqualToString:@"z"])
                {
                    if ([self sendAction:NSSelectorFromString(@"undo:") to:nil from:self])
                        return;
                }
                else if ([inputKey isEqualToString:@"a"])
                {
                    if ([self sendAction:@selector(selectAll:) to:nil from:self])
                        return;
                }
            }
            else if ((event.modifierFlags & NSDeviceIndependentModifierFlagsMask) == (NSCommandKeyMask | NSShiftKeyMask) ||
                     (event.modifierFlags & NSDeviceIndependentModifierFlagsMask) == (NSCommandKeyMask | NSShiftKeyMask | NSAlphaShiftKeyMask))
            {
                if ([inputKey isEqualToString:@"z"])
                {
                    if ([self sendAction:NSSelectorFromString(@"redo:") to:nil from:self])
                        return;
                }
            }
        }
        [super sendEvent:event];
    }
    

    【讨论】:

      【解决方案5】:

      Swift 3 中的 Thomas Kilian 解决方案。

      private let commandKey = NSEventModifierFlags.command.rawValue
      private let commandShiftKey = NSEventModifierFlags.command.rawValue | NSEventModifierFlags.shift.rawValue
      override func performKeyEquivalent(with event: NSEvent) -> Bool {
        if event.type == NSEventType.keyDown {
          if (event.modifierFlags.rawValue & NSEventModifierFlags.deviceIndependentFlagsMask.rawValue) == commandKey {
          switch event.charactersIgnoringModifiers! {
          case "x":
            if NSApp.sendAction(#selector(NSText.cut(_:)), to:nil, from:self) { return true }
          case "c":
            if NSApp.sendAction(#selector(NSText.copy(_:)), to:nil, from:self) { return true }
          case "v":
            if NSApp.sendAction(#selector(NSText.paste(_:)), to:nil, from:self) { return true }
          case "z":
            if NSApp.sendAction(Selector(("undo:")), to:nil, from:self) { return true }
          case "a":
            if NSApp.sendAction(#selector(NSResponder.selectAll(_:)), to:nil, from:self) { return true }
          default:
            break
          }
        }
        else if (event.modifierFlags.rawValue & NSEventModifierFlags.deviceIndependentFlagsMask.rawValue) == commandShiftKey {
          if event.charactersIgnoringModifiers == "Z" {
            if NSApp.sendAction(Selector(("redo:")), to:nil, from:self) { return true }
          }
        }
      }
      return super.performKeyEquivalent(with: event)
      }
      

      【讨论】:

      • swift 4 发生了一些变化。但只需单击修复即可解决。 ✌️
      【解决方案6】:

      Xcode10/Swift 4.2 解决方案:

      import Cocoa
      
      extension NSTextView {
      override open func performKeyEquivalent(with event: NSEvent) -> Bool {
          let commandKey = NSEvent.ModifierFlags.command.rawValue
          let commandShiftKey = NSEvent.ModifierFlags.command.rawValue | NSEvent.ModifierFlags.shift.rawValue
          if event.type == NSEvent.EventType.keyDown {
              if (event.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue) == commandKey {
                  switch event.charactersIgnoringModifiers! {
                  case "x":
                      if NSApp.sendAction(#selector(NSText.cut(_:)), to:nil, from:self) { return true }
                  case "c":
                      if NSApp.sendAction(#selector(NSText.copy(_:)), to:nil, from:self) { return true }
                  case "v":
                      if NSApp.sendAction(#selector(NSText.paste(_:)), to:nil, from:self) { return true }
                  case "z":
                      if NSApp.sendAction(Selector(("undo:")), to:nil, from:self) { return true }
                  case "a":
                      if NSApp.sendAction(#selector(NSResponder.selectAll(_:)), to:nil, from:self) { return true }
                  default:
                      break
                  }
              } else if (event.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue) == commandShiftKey {
                  if event.charactersIgnoringModifiers == "Z" {
                      if NSApp.sendAction(Selector(("redo:")), to:nil, from:self) { return true }
                  }
              }
          }
          return super.performKeyEquivalent(with: event)
      }
      }
      

      【讨论】:

      • 这对我来说效果很好,使用 Swift 5.3。但是,我还必须在创建 TextView 时添加textView.allowsUndo = true。谢谢
      【解决方案7】:

      用于 Thomas Kilian 解决方案的 Swift 4.2

      class MTextField: NSSecureTextField {
      
          private let commandKey = NSEvent.ModifierFlags.command.rawValue
          private let commandShiftKey = NSEvent.ModifierFlags.command.rawValue | NSEvent.ModifierFlags.shift.rawValue
      
          override func performKeyEquivalent(with event: NSEvent) -> Bool {
              if event.type == NSEvent.EventType.keyDown {
                  if (event.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue) == commandKey {
                      switch event.charactersIgnoringModifiers! {
                      case "x":
                          if NSApp.sendAction(#selector(NSText.cut(_:)), to:nil, from:self) { return true }
                      case "c":
                          if NSApp.sendAction(#selector(NSText.copy(_:)), to:nil, from:self) { return true }
                      case "v":
                          if NSApp.sendAction(#selector(NSText.paste(_:)), to:nil, from:self) { return true }
                      case "z":
                          if NSApp.sendAction(Selector(("undo:")), to:nil, from:self) { return true }
                      case "a":
                          if NSApp.sendAction(#selector(NSResponder.selectAll(_:)), to:nil, from:self) { return true }
                      default:
                          break
                      }
                  }
                  else if (event.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue) == commandShiftKey {
                      if event.charactersIgnoringModifiers == "Z" {
                          if NSApp.sendAction(Selector(("redo:")), to:nil, from:self) { return true }
                      }
                  }
              }
              return super.performKeyEquivalent(with: event)
          }
      
      }
      

      【讨论】:

        【解决方案8】:

        这里有一个快速的分步指南,基于 @Adrian、Travis B 和 Thomas Kilian 的出色回答。

        我们的目标是继承 NSApplication,而不是 NSTextField。创建此类后,请按照 Adrian 所述,将其链接到 Info.plist 的“主要类”设置中。与 Objective-C 的人相反,我们 swiftler 将不得不在 principalClass 配置中添加一个额外的前缀。所以,因为我的项目被称为“Foo”,我将把“Principal Class”设置为“Foo.MyApplication”。否则,您将收到“Class MyApplication not found”运行时错误。

        MyApplication 的内容如下(复制并改编自目前给出的所有答案)

        import Cocoa
        
        class MyApplication: NSApplication {
            override func sendEvent(event: NSEvent) {
                if event.type == NSEventType.KeyDown {
                    if (event.modifierFlags & NSEventModifierFlags.DeviceIndependentModifierFlagsMask == NSEventModifierFlags.CommandKeyMask) {
                        switch event.charactersIgnoringModifiers!.lowercaseString {
                        case "x":
                            if NSApp.sendAction(Selector("cut:"), to:nil, from:self) { return }
                        case "c":
                            if NSApp.sendAction(Selector("copy:"), to:nil, from:self) { return }
                        case "v":
                            if NSApp.sendAction(Selector("paste:"), to:nil, from:self) { return }
                        case "z":
                            if NSApp.sendAction(Selector("undo:"), to:nil, from:self) { return }
                        case "a":
                            if NSApp.sendAction(Selector("selectAll:"), to:nil, from:self) { return }
                        default:
                            break
                        }
                    }
                    else if (event.modifierFlags & NSEventModifierFlags.DeviceIndependentModifierFlagsMask == (NSEventModifierFlags.CommandKeyMask | NSEventModifierFlags.ShiftKeyMask)) {
                        if event.charactersIgnoringModifiers == "Z" {
                            if NSApp.sendAction(Selector("redo:"), to:nil, from:self) { return }
                        }
                    }
                }
                return super.sendEvent(event)
            }
        
        }
        

        【讨论】:

          【解决方案9】:

          我解释了在 XCode 8 / Swift 3 中什么对我有用。

          我在我的项目文件夹MyApp 中创建了MyApplication.swift

          import Foundation
          import Cocoa
          
          class MyApplication: NSApplication {
              override func sendEvent(_ event: NSEvent) {
                  if event.type == NSEventType.keyDown {
          
                      if (event.modifierFlags.contains(NSEventModifierFlags.command)) {
                          switch event.charactersIgnoringModifiers!.lowercased() {
                          case "x":
                              if NSApp.sendAction(#selector(NSText.cut(_:)), to:nil, from:self) { return }
                          case "c":
                              if NSApp.sendAction(#selector(NSText.copy(_:)), to:nil, from:self) { return }
                          case "v":
                              if NSApp.sendAction(#selector(NSText.paste(_:)), to:nil, from:self) { return }
                          case "a":
                              if NSApp.sendAction(#selector(NSText.selectAll(_:)), to:nil, from:self) { return }
                          default:
                              break
                          }
                      }
                  }
                  return super.sendEvent(event)
              }
          
          }
          

          然后将Info.plist Principal class 更改为MyApp.MyApplication。构建并运行以验证我的文本字段和文本视图是否支持 Cmd + XCmd + CCmd + VCmd + A

          【讨论】:

          • 这条指令非常清晰明了。轻松适应并在 Swift 3 上工作
          • 很好的解决方案!如果您对主要课程有疑问,请尝试此帖子:stackoverflow.com/a/27144383/6662165
          【解决方案10】:

          根本不需要添加新的类、扩展或任何代码。

          1. 只需在其中一个菜单中添加一个新的 MenuItem,并将它们命名为“复制”、“剪切”和“粘贴”。
          2. 为每个项目添加正确的快捷键。
          3. 按住 Control 并拖动以将它们连接到第一响应者下列出的相应方法。

          这里的好处是项目不会对您的用户隐藏,与创建新类并将所有现有的 TextFields 重新分配给它相比,这需要更少的时间。

          【讨论】:

            【解决方案11】:

            大约 1 小时前,我偶然发现了同样的问题。 您无需编写任何代码。我可以在 Interface Builder 中做到这一点:

            • 创建包含剪切/复制/粘贴菜单项的菜单(例如“编辑”)
            • 将 CMD 键的 KeyEquivalent 添加到“编辑”菜单中(不知道,如果真的需要,我只是从另一个项目中复制了结构)
            • 将 KeyEquivalents 添加到这些菜单项(CMD + X 等)
            • FirstRespondercut:copy:paste: 函数链接到相应的菜单项

            这对我有用。 不幸的是,当您隐藏“编辑”菜单(刚刚尝试过)时,这种(默认)行为似乎不起作用。

            【讨论】:

              【解决方案12】:

              这是 Travis 的 C# 答案,用于 Xamarin.Mac:

                  public override bool PerformKeyEquivalent (AppKit.NSEvent e)
                  {
                      if (e.Type == NSEventType.KeyDown) {
                          var inputKey = e.CharactersIgnoringModifiers.ToLower ();
                          if (   (e.ModifierFlags & NSEventModifierMask.DeviceIndependentModifierFlagsMask) == NSEventModifierMask.CommandKeyMask
                              || (e.ModifierFlags & NSEventModifierMask.DeviceIndependentModifierFlagsMask) == (NSEventModifierMask.CommandKeyMask | NSEventModifierMask.AlphaShiftKeyMask)) {
                              switch (inputKey) {
                              case "x":
                                  NSApplication.SharedApplication.SendAction (new Selector ("cut:"), null, this);
                                  return true;
                              case "c":
                                  NSApplication.SharedApplication.SendAction (new Selector ("copy:"), null, this);
                                  return true;
                              case "v":
                                  NSApplication.SharedApplication.SendAction (new Selector ("paste:"), null, this);
                                  return true;
                              case "z":
                                  NSApplication.SharedApplication.SendAction (new Selector ("undo:"), null, this);
                                  return true;
                              case "a":
                                  NSApplication.SharedApplication.SendAction (new Selector ("selectAll:"), null, this);
                                  return true;
                              }
                          } else if (   (e.ModifierFlags & NSEventModifierMask.DeviceIndependentModifierFlagsMask) == (NSEventModifierMask.CommandKeyMask | NSEventModifierMask.ShiftKeyMask)
                                     || (e.ModifierFlags & NSEventModifierMask.DeviceIndependentModifierFlagsMask) == (NSEventModifierMask.CommandKeyMask | NSEventModifierMask.ShiftKeyMask | NSEventModifierMask.AlphaShiftKeyMask)) {
                              switch (inputKey) {
                              case "z":
                                  NSApplication.SharedApplication.SendAction (new Selector ("redo:"), null, this);
                                  return true;
                              }
                          }
                      }
                      return base.PerformKeyEquivalent(e);
                  }
              

              【讨论】:

                【解决方案13】:

                根据Thomas Kilian的回答,你实际上可以为 NSTextField 创建一个扩展

                let commandKey = NSEvent.ModifierFlags.command.rawValue
                let commandShiftKey = NSEvent.ModifierFlags.command.rawValue | NSEvent.ModifierFlags.shift.rawValue
                
                extension NSTextField {
                    func performEditingKeyEquivalent(with event: NSEvent) -> Bool {
                        guard event.type == NSEvent.EventType.keyDown else { return false }
                
                        if (event.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue) == commandKey {
                            if let character = event.charactersIgnoringModifiers {
                                switch character {
                                case "x":
                                    if NSApp.sendAction(#selector(NSText.cut(_:)), to: nil, from: self) { return true }
                                case "c":
                                    if NSApp.sendAction(#selector(NSText.copy(_:)), to: nil, from: self) { return true }
                                case "v":
                                    if NSApp.sendAction(#selector(NSText.paste(_:)), to: nil, from: self) { return true }
                                case "z":
                                    if NSApp.sendAction(Selector(("undo:")), to: nil, from: self) { return true }
                                case "a":
                                    if NSApp.sendAction(#selector(NSResponder.selectAll(_:)), to: nil, from: self) { return true }
                                default:
                                    break
                                }
                            }
                        } else if (event.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue) == commandShiftKey {
                            if event.charactersIgnoringModifiers == "Z" {
                                if NSApp.sendAction(Selector(("redo:")), to: nil, from: self) { return true }
                            }
                        }
                
                        return false
                    }
                }
                

                在以下示例中,实际上可以将 NSTextField 替换为任何 NSTextField 继承类(例如 NSSearchFieldNSSecureTextField)以获得新功能

                class SearchField: NSTextField {
                    override func performKeyEquivalent(with event: NSEvent) -> Bool {
                        if performEditingKeyEquivalent(with: event) {
                            return true
                        }
                
                        return super.performEditingKeyEquivalent(with: event)
                    }
                }
                

                【讨论】:

                  【解决方案14】:

                  感谢您提供此解决方案!它对我帮助很大,所以我决定贡献一些代码,希望它可以帮助别人。上面建议的解决方案在我将其转换为Swift 4.2 后完美运行。然后我稍微重构了代码。我认为这有点清洁。这是Swift 4.2 兼容的:

                  // NSEventExtensions.swift
                  
                  import AppKit
                  
                  extension NSEvent {
                      func containsKeyModifierFlags(_ flags: NSEvent.ModifierFlags) -> Bool {
                          switch modifierFlags.intersection(.deviceIndependentFlagsMask) {
                          case [flags]: return true
                          default: return false
                          }
                      }
                  }
                  
                  // SearchFiled.swift
                  
                  import AppKit
                  import Carbon
                  
                  final class SearchField: NSSearchField {
                      override func performKeyEquivalent(with event: NSEvent) -> Bool {
                          switch event.type {
                          case .keyDown: return performKeyDownEquivalent(with: event)
                          default: return super.performKeyEquivalent(with: event)
                          }
                      }
                  
                      // MARK: - private
                  
                      private func performKeyDownEquivalent(with event: NSEvent) -> Bool {
                          if event.containsKeyModifierFlags(.command) {
                              switch Int(event.keyCode) {
                              case kVK_ANSI_X: return NSApp.sendAction(#selector(NSText.cut(_:)), to: nil, from: self)
                              case kVK_ANSI_C: return NSApp.sendAction(#selector(NSText.copy(_:)), to: nil, from: self)
                              case kVK_ANSI_V: return NSApp.sendAction(#selector(NSText.paste(_:)), to: nil, from: self)
                              case kVK_ANSI_Z: return NSApp.sendAction(Selector(("undo:")), to: nil, from: self)
                              case kVK_ANSI_A: return NSApp.sendAction(#selector(NSResponder.selectAll(_:)), to: nil, from: self)
                              default: break
                              }
                          } else if event.containsKeyModifierFlags([.command, .shift]) {
                              switch Int(event.keyCode) {
                              case kVK_ANSI_Z: return NSApp.sendAction(Selector(("redo:")), to: nil, from: self)
                              default: break
                              }
                          }
                          return false
                      }
                  }
                  

                  【讨论】:

                    【解决方案15】:

                    Swift 5 针对NSApplication 子类的解决方案

                    open override func sendEvent(_ event: NSEvent) {
                        if event.type == .keyDown {
                            if event.modifierFlags.contains(.command)  && NSEvent.ModifierFlags.deviceIndependentFlagsMask.contains(.command) {
                                if event.modifierFlags.contains(.shift) && NSEvent.ModifierFlags.deviceIndependentFlagsMask.contains(.shift) {
                                    if event.charactersIgnoringModifiers == "Z" {
                                        if NSApp.sendAction(Selector("redo:"), to:nil, from:self) { return }
                                    }
                                }
                                guard let key = event.charactersIgnoringModifiers else { return super.sendEvent(event) }
                                switch key {
                                case "x":
                                    if NSApp.sendAction(Selector("cut:"), to:nil, from:self) { return }
                                case "c":
                                    if NSApp.sendAction(Selector("copy:"), to:nil, from:self) { return }
                                case "v":
                                    if NSApp.sendAction(Selector("paste:"), to:nil, from:self) { return }
                                case "z":
                                    if NSApp.sendAction(Selector("undo:"), to:nil, from:self) { return }
                                case "a":
                                    if NSApp.sendAction(Selector("selectAll:"), to:nil, from:self) { return }
                                default:
                                    break
                              }
                            }
                        }
                        super.sendEvent(event)
                    }
                    

                    【讨论】:

                      【解决方案16】:

                      Adrian 的解决方案很好,但我认为使用 switch 语句而不是所有那些字符串比较更好,例如:

                          uint const modifierCode = (theEvent.modifierFlags & NSDeviceIndependentModifierFlagsMask);
                          BOOL usingModifiers = ( modifierCode != 0 );
                          //BOOL const usingShiftKey = ((theEvent.modifierFlags & NSShiftKeyMask) != 0);
                          //BOOL const usingCommandKey = ((theEvent.modifierFlags & NSCommandKeyMask) != 0);
                          NSString * ch = [theEvent charactersIgnoringModifiers];
                          if ( ( usingModifiers ) && ( ch.length == 1 ) ) switch ( [ch characterAtIndex:0] )
                          {
                              case 'x':
                                  if ( modifierCode == NSCommandKeyMask ) [m cut]; // <-- m = model
                                  break;
                              case 'c':
                                  if ( modifierCode == NSCommandKeyMask ) [m copy];
                                  break;
                              case 'v':
                                  if ( modifierCode == NSCommandKeyMask ) [m paste];
                                  break;
                              case 'z':
                                  if ( modifierCode == NSCommandKeyMask ) [m undo];
                                  break;
                              case 'Z':
                                  if ( modifierCode == ( NSCommandKeyMask | NSShiftKeyMask ) ) [m redo];
                                  break;
                              default: // etc.
                                  break;
                          }
                          else switch ( theEvent.keyCode ) // <-- for independent keycodes!
                          {
                              case kVK_Home:
                                  [m moveToBeginningOfDocument:nil];
                                  break;
                              case kVK_End: // etc!
                      

                      【讨论】:

                        猜你喜欢
                        • 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
                        相关资源
                        最近更新 更多