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

时间:2011-05-11 09:09:50

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

我将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 个答案:

答案 0 :(得分:5)

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

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

答案 1 :(得分:3)

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

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

  1. -[NSApplication sendEvent:]
  2. -[NSWindow sendEvent:]
  3. -[NSWindow keyDown:]
  4. -[NSWindow doCommandBySelector:]
  5. -[NSResponder doCommandBySelector:]
  6. 此后发出系统提示音,表示没有可以处理keyDown事件的对象。

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

    修复方法是在致电-makeKeyAndOrderFront:后在私人全屏窗口上致电-[NSView enterFullScreenMode:withOptions:]

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

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

    NSView上的全屏模式由AppKit从原始窗口中删除视图,然后将其设置为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];