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

时间:2017-02-22 13:49:56

标签: 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
    }

}

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的解释。

3 个答案:

答案 0 :(得分:6)

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

  

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

     

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

     

NSResponder声明但未实现此方法。

NSWindow实现cancelOperation:,而下一个响应者(窗口控制器)未检查cancelOperation:的实现。 cancel:消息确实到达窗口控制器。实施

- (void)cancel:(id)sender
{
    NSLog(@"cancel");
}

会奏效。 cancel:消息不会从超类继承,因此自动完成功能不会建议它。

答案 1 :(得分:1)

当我需要这样的行为时,我通过重写NSWindow对象的keyDown来实现它。

即。如下所示:

- (void)keyDown:(NSEvent *)theEvent
{
    int k = [theEvent keyCode];
    if (k == kVK_Escape)
    {
        [self close];
        return;
    }

    [super keyDown:theEvent];
}

答案 2 :(得分:1)

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

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

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