如何通过按 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)?的主要内容,如果未能解决你的问题,请参考以下文章