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

aya*_*aio -1 macos cocoa first-responder nsviewcontroller nswindowcontroller

问题

我希望用户可以通过按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
    }

}
Run Code Online (Sandbox Code Playgroud)

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
    }

}
Run Code Online (Sandbox Code Playgroud)

我试过的

我的问题是MyWindowController NSWindow是.initialFirstResponder我希望targetView(一个NSTableView)的内容成为第一个响应者的时候 - keyDown我想这样我可以使用这种方式,并从那里发送关闭命令到窗口.但这似乎并不是最佳选择.

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

我也尝试将其添加到MyWindowController:

override func cancelOperation(_ sender: Any?) {
    print("yeah, let's close!")
}
Run Code Online (Sandbox Code Playgroud)

但这仅在用户首先点击窗口背景然后点击ESC 时才有效,并且仍然会发出错误声音.这实际上是让我觉得这个问题是关于第一个响应者在窗口的问题.

你会如何实现这一目标?当然,我知道用户已经可以用CMD + W关闭窗口了,但是我真的很想解决这个问题.

请注意,代码示例在Swift中,但我也可以使用Objective-C接受解释.

Wil*_*eke 9

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

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

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

NSResponder 声明但不实现此方法.

NSWindow实现cancelOperation:和下一个响应者,窗口控制器,没有检查实现cancelOperation:.该cancel:消息不会到达窗口控制器.实施

- (void)cancel:(id)sender
{
    NSLog(@"cancel");
}
Run Code Online (Sandbox Code Playgroud)

将工作.该cancel:消息不是从超类继承的,因此自动完成不建议它.


Eln*_*mov 5

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

@objc func cancel(_ sender: Any?) {
    close()
}
Run Code Online (Sandbox Code Playgroud)

我之前尝试过,但没有该@objc部分,它不起作用。所以不要省略它。