如何在退出全屏模式后接收 NSView 的键盘事件?

5

我对NSView进行了子类化,并在应用程序完成启动后启动全屏模式。该视图可作为应用程序委托中的fooView属性使用。

// AppDelegate.m
- (void)applicationDidFinishLaunching:(NSNotification*)notification {
  [[self window] makeKeyAndOrderFront:self];
  [[self fooView] enterFullScreenMode:[NSScreen mainScreen] withOptions:nil];
}

该类FooView本身实现了以下功能。
// FooView.m
- (void)keyDown:(NSEvent*)event {
  NSLog(@"%@ %@ - %@", self.className, NSStringFromSelector(_cmd), event);
  [self interpretKeyEvents:[NSArray arrayWithObject:event]];
}
- (void)cancelOperation:(id)sender {
  NSLog(@"%@ %@ - %@", self.className, NSStringFromSelector(_cmd), sender);
  [self exitFullScreenModeWithOptions:nil];
}

退出全屏模式后,视图不再接收键盘事件。为什么?

编辑:
似乎与我退出全屏模式的方式有关。当我点击视图(而不是窗口)时,keyDown:cancelOperation:会如下响应。

2个回答

5
问题在于包含视图的窗口没有接收到任何键盘事件。需要在退出全屏模式后使窗口成为第一响应者。
- (void)cancelOperation:(id)sender {
  NSLog(@"%@ %@ - %@", self.className, NSStringFromSelector(_cmd), sender);
  [self exitFullScreenModeWithOptions:nil];
  [self.window makeFirstResponder:self];
}

3
我遇到了类似的问题。在调用-[NSView enterFullScreenMode:withOptions:]后,我无法收到所有的keyDown:事件(特别是Esc键按下),直到我点击全屏视图。
通过在-[NSResponder doCommandBySelector:]上设置一个符号断点,我找到了问题所在,它显示了堆栈跟踪:
  1. -[NSApplication sendEvent:]
  2. -[NSWindow sendEvent:]
  3. -[NSWindow keyDown:]
  4. -[NSWindow doCommandBySelector:]
  5. -[NSResponder doCommandBySelector:]
此时会播放系统提示音,表明没有对象可以处理keyDown事件。
查看汇编输出显示,它正在检查窗口的键和主要状态。根本原因是私有的全屏窗口(由AppKit附加到视图上)不会自动成为主窗口或关键窗口,因此无法像预期的那样接收键事件。
修复方法是在调用-[NSView enterFullScreenMode:withOptions:]后调用私有全屏窗口的-makeKeyAndOrderFront:方法。
这是通过-[NSObject performSelector:withObject:afterDelay:]来实现的,因为直到运行循环的下一次迭代,视图的window属性才设置为私有全屏窗口(而不是其原始窗口)。我不确定还有其他引用私有窗口的方法。
   [self.view.window performSelector:@selector(makeKeyAndOrderFront:)
                          withObject:nil
                          afterDelay:0];

NSView上的全屏模式是通过AppKit将视图从其原始窗口中移除,然后将其设置为类型为_NSFullScreenWindow的私有窗口的contentView来实现的(该窗口在其他方面不具有标题栏)。当视图处于全屏模式时,可以通过选择Debug>View Debugging>Capture View Hierarchy来查看。退出全屏模式将其从_NSFullScreenWindow中移除,并将其设置为原始窗口的contentView编辑: 我删除了上面描述的先前修复方法,因为在重新配置我处理按键事件的方式之后它不再起作用。现在应用程序中的按键事件通过窗口的内容视图处理,该内容视图是自定义的NSView子类。应用启动时,将initialResponderfirstResponder都设置为窗口的contentView。在调用后必须重新设置这两个窗口属性:
-[NSView exitFullScreenModeWithOptions:]

由于AppKit在全屏过程中会更改它们,因此需要注意。

在我的NSView子类中,处理键盘事件以及全屏:

[self exitFullScreenModeWithOptions:nil];
[self.window setInitialResponder:self];
[self.window makeFirstResponder:self];

我曾遇到一个问题,即当视图处于全屏模式时,键盘事件仍无法在10.9.5上正常工作。问题在于用于全屏模式的私有窗口未将其下一个响应者设置为原始窗口的下一个响应者,就像AppKit在10.11+上自动完成的那样(我不确定在10.10上的行为)。以下操作解决了该问题:
// Get a reference to the window controller from the window BEFORE full screen mode is enabled
// and the view's window is set to the private AppKit "_NSFullScreenWindow" instance.
NSWindowController *windowController = self.window.windowController;

// Enable full screen mode on the view
[self enterFullScreenMode:screen withOptions:opts];

// Compatibility: On 10.9.5 the window controller is not set as the nextResponder on the private full-screen window automatically
// Set the existing window controller as the next responder for the private full screen window to ensure it is placed in the responder chain
[self.window setNextResponder:windowController];

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接