NSResponder中interpretKeyEvents为何忽略部分重复按键?
问题:NSView长按部分按键无法重复触发insertText
我有一个NSView,会在keyDown事件中调用interpretKeyEvents。对于'a'这类按键,长按会重复触发insertText调用;但对于'x'这类按键,insertText仅被调用一次。我希望长按所有按键都能触发insertText,推测这类按键的长按被识别为其他操作,但未找到相关原因。我已按文档实现doCommandBySelector,但该方法从未被调用。
以下是复现该行为的最小示例代码:
// clang++ -framework AppKit -o proof-of-concept proof-of-concept.mm #include <stdio.h> #import <Cocoa/Cocoa.h> @interface AppView : NSView<NSTextInputClient> @end @implementation AppView - (void)keyDown:(NSEvent *)event { printf("keydown\n"); [self interpretKeyEvents:[NSArray arrayWithObject:event]]; } - (void)insertText:(id)string replacementRange:(NSRange)replacementRange { printf("insertText\n"); } - (BOOL)acceptsFirstResponder { return YES; } - (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange {} - (void)unmarkText {} - (NSRange)selectedRange { return NSMakeRange(NSNotFound, 0); } - (NSRange)markedRange { return NSMakeRange(NSNotFound, 0); } - (BOOL)hasMarkedText { return NO; } - (nullable NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range actualRange:(nullable NSRangePointer)actualRange { return nil; } - (NSArray<NSAttributedStringKey> *)validAttributesForMarkedText { return [NSArray array]; } - (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(nullable NSRangePointer)actualRange { return NSMakeRect(0, 0, 0, 0); } - (NSUInteger)characterIndexForPoint:(NSPoint)point { return 0; } @end int main(int argc, char **argv, char **envp, char **apple) { [NSApplication sharedApplication]; [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; NSRect windowDimensions = NSMakeRect(0, 0, 300, 300); NSWindow *window = [[NSWindow alloc] initWithContentRect:windowDimensions styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskResizable backing:NSBackingStoreBuffered defer:NO]; AppView *windowView = [[AppView alloc] initWithFrame:windowDimensions]; [window setContentView:windowView]; [window makeKeyAndOrderFront:nil]; [NSApp run]; return 0; }
原因与解决方案
原因
macOS系统默认将X、C、V这类按键的长按行为关联到剪切、复制、粘贴等文本编辑快捷操作。当长按这些按键时,interpretKeyEvents会遵循系统按键映射规则,把重复事件导向命令处理流程,而非insertText。你实现的doCommandBySelector未被调用,是因为系统在处理这类重复按键时,并没有生成对应的命令选择器事件,直接跳过了该流程。
解决方案
方案1:直接在keyDown中处理文本插入
放弃使用interpretKeyEvents,直接从NSEvent提取字符并调用insertText,所有按键(包括重复事件)都会触发目标方法:
- (void)keyDown:(NSEvent *)event { printf("keydown\n"); NSString *charStr = event.charactersIgnoringModifiers; if (charStr.length > 0) { [self insertText:charStr replacementRange:NSMakeRange(NSNotFound, 0)]; } }
方案2:拦截按键等价事件
如果坚持使用interpretKeyEvents,可以重写performKeyEquivalent:方法,拦截指定按键的命令处理,强制导向insertText:
- (BOOL)performKeyEquivalent:(NSEvent *)event { NSString *charStr = event.charactersIgnoringModifiers; // 可根据需求添加更多需要处理的按键 NSArray *targetChars = @[@"x", @"c", @"v"]; if ([targetChars containsObject:charStr]) { [self insertText:charStr replacementRange:NSMakeRange(NSNotFound, 0)]; return YES; } return [super performKeyEquivalent:event]; }
内容的提问来源于stack exchange,提问作者jimspin
相关产品推荐
相关产品推荐

