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:请在以下内容中做出响应。

4

2 回答 2

5

问题是包含视图的窗口确实收到了任何键盘事件。离开全屏模式后,需要使窗口成为第一响应者。

- (void)cancelOperation:(id)sender {
  NSLog(@"%@ %@ - %@", self.className, NSStringFromSelector(_cmd), sender);
  [self exitFullScreenModeWithOptions:nil];
  [self.window makeFirstResponder:self];
}
于 2011-07-05T08:13:34.923 回答
3

我遇到了类似的问题。调用后,-[NSView enterFullScreenMode:withOptions:]我无法接收所有 keyDown: 事件(特别是 Escape key down),直到我单击全屏视图。

我通过在 上设置符号断点来跟踪问题,该断点-[NSResponder doCommandBySelector:]显示了堆栈跟踪:

  1. -[NSApplication sendEvent:]
  2. -[NSWindow sendEvent:]
  3. -[NSWindow keyDown:]
  4. -[NSWindow doCommandBySelector:]
  5. -[NSResponder doCommandBySelector:]

此后,系统会发出哔哔声,表明没有对象可以处理 keyDown 事件。

查看程序集输出表明它正在检查窗口的键和主要状态。根本问题是私有全屏窗口(由 AppKit 附加的视图)不会自动成为主窗口或关键窗口,因此不会按预期接收关键事件。

修复方法是在调用-makeKeyAndOrderFront:后调用私有全屏窗口-[NSView enterFullScreenMode:withOptions:]

这样做-[NSObject performSelector:withObject:afterDelay:]是因为直到运行循环的下一次迭代才将视图的window属性设置为私有全屏窗口(而不是其原始窗口)。我不确定引用私人窗口的另一种方式。

   [self.view.window performSelector:@selector(makeKeyAndOrderFront:)
                          withObject:nil
                          afterDelay:0];

AppKit 工作的全屏模式NSView从其原始窗口中删除视图,然后将其设置为contentView类型的私有窗口_NSFullScreenWindow(其中没有标题栏)。这可以通过Debug > View Debugging > Capture View Hierarchy在视图处于全屏模式时选择来查看。退出全屏会将其从 中删除并将其_NSFullScreenWindow设置为contentView原始窗口的。

编辑:

我删除了上面描述的之前的修复,因为在我重新配置了处理关键事件的方式后它不再起作用。现在应用程序中的关键事件是通过窗口的内容视图处理的,它是一个自定义的 NSView 子类。contentView 是窗口initialResponderfirstResponder应用程序启动时的。调用后必须重新设置这两个窗口属性:

-[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];
于 2017-07-25T00:51:39.787 回答