我遇到了类似的问题。调用后,-[NSView enterFullScreenMode:withOptions:]
我无法接收所有 keyDown: 事件(特别是 Escape key down),直到我单击全屏视图。
我通过在 上设置符号断点来跟踪问题,该断点-[NSResponder doCommandBySelector:]
显示了堆栈跟踪:
-[NSApplication sendEvent:]
-[NSWindow sendEvent:]
-[NSWindow keyDown:]
-[NSWindow doCommandBySelector:]
-[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 是窗口initialResponder
和firstResponder
应用程序启动时的。调用后必须重新设置这两个窗口属性:
-[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];