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

发布于 2024-11-06 13:29:46 字数 897 浏览 6 评论 0原文

我对 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: 会做出以下响应。

I subclass an NSView and start full screen mode when the application finished launching. The view is available as the property fooView in the application delegate.

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

The class FooView itself implements the following functions.

// 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];
}

After leaving the fullscreen mode, the view no longer receives keyboard events. Why?

Edit:
It seems to have something to do with how I exit the fullscreen mode. When I click into the view (not the window) the keyDown: and cancelOperation: do respond in the following.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

空宴 2024-11-13 13:29:46

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

- (void)cancelOperation:(id)sender {
  NSLog(@"%@ %@ - %@", self.className, NSStringFromSelector(_cmd), sender);
  [self exitFullScreenModeWithOptions:nil];
  [self.window makeFirstResponder:self];
}

The problem was that the window containing the view did receive any keyboard events. One needs to make the window the first responder after leaving the full screen mode.

- (void)cancelOperation:(id)sender {
  NSLog(@"%@ %@ - %@", self.className, NSStringFromSelector(_cmd), sender);
  [self exitFullScreenModeWithOptions:nil];
  [self.window makeFirstResponder:self];
}
成熟的代价 2024-11-13 13:29:46

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

我通过在 -[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 (其中没有标题栏)。这可以通过选择“调试”来看到。查看调试>当视图处于全屏模式时捕获视图层次结构。退出全屏会将其从 _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];

I was having a similar issue. After calling -[NSView enterFullScreenMode:withOptions:] I wasn't able to receive all keyDown: events (specifically Escape key down) until I clicked the full screen view.

I tracked down the issue by setting an symbolic breakpoint on -[NSResponder doCommandBySelector:], which showed the stack trace:

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

After this point the system beep was played indicating there was no object that could handle the keyDown event.

Looking at the assembly output showed that it was checking for the window's key and main status. The root issue was that the private full screen window (that the view is attached to by AppKit) is not automatically made the main window or key window and thus does not receive key events as expected.

The fix was to call -makeKeyAndOrderFront: on the private full screen window after calling -[NSView enterFullScreenMode:withOptions:].

This was doing using -[NSObject performSelector:withObject:afterDelay:] because it isn't until the next iteration of the run loop that the view's window property is set to the private full screen window (instead of its original window). I'm not sure of another way to reference the private window.

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

Full screen mode on NSView works by AppKit removing the view from its original window, then setting it as the contentView of a private window of type _NSFullScreenWindow (which among other things does not have a title bar). This can be seen by selecting Debug > View Debugging > Capture View Hierarchy while the view is in full screen mode. Exiting full screen removes it from the _NSFullScreenWindow and sets it as the contentView of the original window.

EDIT:

I removed my previous fix described above as it was no longer working after I reconfigured the way I handled key events. Now key events in the app are handled via the window's content view, which is a custom NSView subclass. The contentView is made the window's initialResponder and firstResponder at app launch. Both these window properties must be set again after calling:

-[NSView exitFullScreenModeWithOptions:]

because AppKit changes them during the full screen process.

In my NSView subclass that handles key events as well as full-screen:

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

I also ran into an issue where keyboard events were still not working on 10.9.5 when the view was in full screen mode.

The issue was that the private window used for full-screen mode did not have its next responder set to the original window's next responder, like AppKit does automatically on 10.11+ (I'm unsure of the behavior on 10.10). The following fixed the issue:

// 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];
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文