如何通过按 ESC 键关闭窗口(NSWindowController)?

Posted

技术标签:

【中文标题】如何通过按 ESC 键关闭窗口(NSWindowController)?【英文标题】:How to close window (NSWindowController) by hitting the ESC key? 【发布时间】:2017-02-22 13:49:56 【问题描述】:

问题

我希望用户能够通过按 ESC 键来关闭窗口,但在这种特定情况下我无法让它工作,按 ESC 会触发错误声音(“不,你不能这样做” macOS bloop),没有任何反应。

上下文

我正在创建 NSWindowController 的子类,它本身创建 NSViewController 子类的实例并将其设置在视图中。两个控制器都有自己的 xib 文件。

NSWindowController:

final class MyWindowController: NSWindowController, NSWindowDelegate 

    @IBOutlet weak var targetView: MainView!

    let myVC: MyViewController!
    var params: SomeParams!

    override func windowDidLoad() 
        super.windowDidLoad()
        myVC = MyViewController(someParams: params)
        myVC.view.setFrameSize(targetView.frame.size)
        myVC.view.setBoundsSize(targetView.bounds.size)
        targetView.addSubview(myVC.view)
    

    override var windowNibName: String! 
        return "MyWindowController"
    

    convenience init(someParams params: SomeType) 
        self.init(window: nil)
        self.params = params
    


NSViewController:

final class MyViewController: NSViewController 

    convenience init(someParams params: SomeType) 
        // do stuff with the params
    

    override func viewDidLoad() 
        super.viewDidLoad()
        // configure stuff for the window
    


我尝试过的

假设我的问题是 MyWindowController NSWindow 是 .initialFirstResponder,而我希望 targetView(一个 NSTableView)的内容成为第一响应者 - 这样我可以使用keyDown,我猜,然后从那里将关闭命令发送到窗口。不过,这似乎并不理想。

我尝试通过在 MyWindowController 的 windowDidLoad 中使用 window?.makeFirstResponder(theView) 来强制视图控制器视图成为第一响应者,但没有任何改变。

我也尝试将其添加到 MyWindowController:

override func cancelOperation(_ sender: Any?) 
    print("yeah, let's close!")

但这只有在用户首先点击窗口背景时才有效,然后按 ESC,它仍然会发出错误声音。这实际上是让我认为问题在于第一响应者在窗口上的原因。

问题

您将如何实现这一目标?当然,我知道用户已经可以用 CMD+W 关闭窗口,但我还是很想解决这个问题。

请注意,代码示例是用 Swift 编写的,但我也可以接受使用 Objective-C 的解释。

【问题讨论】:

试试- (void)cancel:(id)sender 谢谢,但它没有出现在 Xcode 的自动补全 (screenshot) 中。 在这种情况下,您可以自己输入或使用cancelOperation 并删除“操作”。 @Willeke 哇。有用!我想 Xcode 8 中有一个错误,自动完成应该肯定会显示出来。谢谢!请添加它作为答案。 :) 【参考方案1】:

cancelOperation 的文档解释了cancelOperation 应该如何工作:

此方法绑定到 Escape 和 Command-。 (句号)键。键窗口首先在视图层次结构中搜索其等效键为 Escape 或 Command-.(无论输入哪个键)的视图。如果这些视图都没有处理等效键,则窗口会向第一响应者发送默认操作消息cancelOperation:,然后消息从那里向上传播到响应者链。

如果响应者链中没有响应者实现cancelOperation:,则键窗口在视图层次结构中搜索其等效键为 Escape 的视图(请注意,如果原始等效键为 Escape,则这可能是多余的)。如果没有找到这样的响应者,则将cancel: 操作消息发送到实现它的响应者链中的第一个响应者。

NSResponder 声明但未实现此方法。

NSWindow 实现 cancelOperation: 并且下一个响应者,窗口控制器,不会检查 cancelOperation: 的实现。 cancel: 消息确实到达了窗口控制器。实施

- (void)cancel:(id)sender

    NSLog(@"cancel");

会起作用。 cancel: 消息不是从超类继承的,因此不建议使用自动补全。

【讨论】:

谢谢,这正是我需要的。【参考方案2】:

这在 Xcode 10 和 Swift 4.2 中对我有用:

@objc func cancel(_ sender: Any?) 
    close()

我之前尝试过,但没有 @objc 部分,它没有工作。所以不要忽略它。

【讨论】:

【参考方案3】:

当我需要这种行为时,我通过覆盖 NSWindow 对象的 keyDown: 来实现它。

即类似于以下内容:

- (void)keyDown:(NSEvent *)theEvent

    int k = [theEvent keyCode];
    if (k == kVK_Escape)
    
        [self close];
        return;
    

    [super keyDown:theEvent];

【讨论】:

我前段时间尝试过 - 但我使用的是class MyWindowController: NSWindowController, NSWindowDelegate,所以我没有看到如何使用 NSWindow 子类... 如果您通过 xib 创建 NSWindow 对象,那么只需使用此方法在某处描述类 MyWindow : NSWindow,并将对象的类设置为 MyWindow 而不是 NSWindow 感谢您提供此信息。我最终实施了 Willeke 的解决方案,这对我来说是完美的。

以上是关于如何通过按 ESC 键关闭窗口(NSWindowController)?的主要内容,如果未能解决你的问题,请参考以下文章

vba中的窗口,如何用esc键实现 窗口右上角的那个关闭功能。(不添加命

WPF学习笔记-如何按ESC关闭窗口

如何强制关闭程序

windows 怎么强制结束任务?

电脑上的设置粘滞键是啥?

笔记本电脑fn键怎么取消