按键按下事件未被调用

9
我有一个名为SurfaceView的自定义NSView。它是NSWindow的contentView,处理基本的鼠标点击和绘图事件。但是,无论我做什么,它都无法处理keyDown函数。我已经覆盖了acceptsFirstResponder,但什么也没有发生。
如果有影响的话,我使用下面所示的自定义NSEvent循环运行应用程序:
NSDictionary* info = [[NSBundle mainBundle] infoDictionary];
NSString* mainNibName = [info objectForKey:@"NSMainNibFile"];

NSApplication* app = [NSApplication sharedApplication];
NSNib* mainNib = [[NSNib alloc] initWithNibNamed:mainNibName bundle:[NSBundle mainBundle]];
[mainNib instantiateNibWithOwner:app topLevelObjects:nil];

[app finishLaunching];

while(true)
{   
    NSEvent* event = [app nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate date] inMode:NSDefaultRunLoopMode dequeue:YES];
    [app sendEvent:event];

    // Some code is execute here every frame to do some tasks...

    usleep(5000);
}

这里是SurfaceView的代码:

@interface SurfaceView : NSView
{
    Panel* panel;
}

@property (nonatomic) Panel* panel;

- (void)drawRect:(NSRect)dirtyRect;
- (BOOL)isFlipped;
- (void)mouseDown:(NSEvent *)theEvent;
- (void)mouseDragged:(NSEvent *)theEvent;
- (void)mouseUp:(NSEvent *)theEvent;
- (void)keyDown:(NSEvent *)theEvent;
- (BOOL)acceptsFirstResponder;
- (BOOL)becomeFirstResponder;

@end

--

@implementation SurfaceView

@synthesize panel;

- (BOOL)acceptsFirstResponder
{
    return YES;
};

- (void)keyDown:(NSEvent *)theEvent
{
    // this function is never called
};

...

@end

以下是我创建视图的步骤:

NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(left, top, wide, tall) styleMask:NSBorderlessWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask backing:NSBackingStoreBuffered defer:NO];

...

[window makeKeyAndOrderFront:nil];

SurfaceView* mainView = [SurfaceView alloc];
[mainView initWithFrame:NSMakeRect(0, 0, wide, tall)];
mainView.panel = panel;
[window setContentView:mainView];
[window setInitialFirstResponder:mainView];
[window setNextResponder:mainView];
[window makeFirstResponder:mainView];

你是否也重写了 -becomeFirstResponder 方法?在调用 makeFirstResponder 后,你可以使用 NSLog(@"%@", window.firstResponder) 来确认 SurfaceView 是否真的是第一响应者。 - Vervious
只是一个快速的提示,删除你接口文件中的所有方法声明,因为它们已经在NSView中声明过了。不需要再次声明它们。 - TheAmateurProgrammer
1
还有,每个方法末尾的分号是什么意思? - TheAmateurProgrammer
@Vervious 是的。而 window.firstResponder 是 SurfaceView。 - user1467310
@theAmateurProgrammer 这是我从C++中学来的一个不好的做法。 - user1467310
3个回答

29

我发现是什么阻止了 keyDown 事件被调用。这是由于 NSBorderlessWindowMask 遮罩,它会阻止窗口成为关键和主要窗口。因此,我创建了一个名为 BorderlessWindowNSWindow 子类:

@interface BorderlessWindow : NSWindow
{
}

@end

@implementation BorderlessWindow

- (BOOL)canBecomeKeyWindow
{
    return YES;
}

- (BOOL)canBecomeMainWindow
{
    return YES;
}

@end

天啊!你救了我的一天! - skywinder
非常感谢您。您为我节省了很多时间。对于那些使用Swift和NSViewController的人,您可以简单地创建一个NSWindow的扩展,并在这些方法上返回true。 - Epic Byte
1
@Epic Byte - 这不正确。根据文档: "您不能使用扩展来覆盖Objective-C类型上的现有方法或属性"。 - Teo Sartori
@TeoSartori 那个评论是在扩展允许覆盖方法时发表的。 - Epic Byte
1
那么值得纠正。在怀疑评论并检查文档之前,我浪费了一些时间没有让它工作。 - Teo Sartori
经过两个小时的搜索,这就是解决方案。 - Swift1

2

除了回答:在IB复选框中检查NSWindow

Title Bar应该被选中。这类似于NSBorderlessWindowMask

enter image description here


0
在我的情况下,我不得不覆盖keyDown方法,同时对NSViewNSWindow进行自定义。在NSWindow中,在keyDown覆盖中,我必须调用。
- (void) keyDown:(NSEvent *) event {

    if (self.firstResponder != self) {
    
        [self.firstResponder keyDown:event];
    }
}

为了让事件到达视图,我当然也需要先在NSWindow上调用makeFirstResponder,并将contentView传递给它:

[window makeFirstResponder: [window contentView] ];

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接