【问题标题】:How to receive keyboard events for a NSView after exiting fullscreen mode?退出全屏模式后如何接收 NSView 的键盘事件?
【发布时间】:2017-07-29 17:28:26
【问题描述】:

当应用程序完成启动时,我将一个 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: 会在下面做出响应。

【问题讨论】:

    标签: objective-c fullscreen nsview keyboard-events nsresponder


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

      我遇到了类似的问题。在调用 -[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(其中没有标题栏) .这可以通过在视图处于全屏模式时选择 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];
      

      【讨论】:

        猜你喜欢
        • 2023-03-19
        • 1970-01-01
        • 2014-12-15
        • 2014-09-27
        • 2017-11-16
        • 1970-01-01
        • 1970-01-01
        • 2019-07-23
        • 2014-03-07
        相关资源
        最近更新 更多