Objective c NSTextField捕获返回键事件两次

Objective c NSTextField捕获返回键事件两次,objective-c,nstextfield,keyboard-navigation,Objective C,Nstextfield,Keyboard Navigation,我有一个文本字段和两个按钮。用户应该能够在编辑完文本字段后按return键,然后根据条件再次按return键激活其中一个按钮。为了让用户清楚地知道,他们可以返回来激活按钮,我临时将return指定为所选按钮的等效键,这将使它发出蓝色的光芒 textfield的已发送操作选择器包含以下代码: switch (self.iNavMode) { case kNavModeNeutral: break; case kNavModeSaveAndNew: [

我有一个文本字段和两个按钮。用户应该能够在编辑完文本字段后按return键,然后根据条件再次按return键激活其中一个按钮。为了让用户清楚地知道,他们可以返回来激活按钮,我临时将return指定为所选按钮的等效键,这将使它发出蓝色的光芒

textfield的已发送操作选择器包含以下代码:

switch (self.iNavMode) {
    case kNavModeNeutral:
        break;
    case kNavModeSaveAndNew:
        [self.window makeFirstResponder:self.btnSaveAndNew];
        [self.btnSaveAndNew setKeyEquivalent:@"\r"];
        break;
    case kNavModeSaveAndNext:
        [self.window makeFirstResponder:self.btnSaveAndNext];
        [self.btnSaveAndNext setKeyEquivalent:@"\r"];
        break;
    default:
        break;
}
然后,所选按钮的动作会击倒相应的按键,这样按钮在退出firstResponder后不会继续发出蓝色的光:

[self.btnSaveAndNext setKeyEquivalent:@""];
- (BOOL) performKeyEquivalent:(NSEvent *)theEvent {
    printf("\nThe keycode is %d", [theEvent keyCode]);
    if ([theEvent keyCode] == 36) {
        ThisProject_AppDelegate *appDelegate = [[NSApplication sharedApplication] delegate];
        id firstResponder = [appDelegate.windowController.window firstResponder];
        if ([firstResponder isKindOfClass:[NSTextView class]]) {
            printf("\nfirstResp is a field editor, a textview.");
            if ([firstResponder delegate] == self) {
                printf("\ntarget textfield is firstResponder.");
                return YES;
            }
        }
        else if ([firstResponder isKindOfClass:[NSButton class]]) {
            printf("\nfirstResp is a button.");
            return YES;
        }
    }
    return NO;
}
问题是,当用户从文本字段返回时,返回键事件不知何故被捕获两次,并且程序自己激活按钮,即使用户实际上没有再次按下返回键


有没有一种方法可以完全捕获并处理第一个返回键事件,这样就不会发生这种情况?

嗯,我有一个难题

我添加了一个布尔属性shouldThisReturn。我在textfield的sent action选择器中添加了一行,将该布尔值设置为yes:

switch (self.iNavMode) {
    case kNavModeNeutral:
        break;
    case kNavModeSaveAndNew:
        [self.window makeFirstResponder:self.btnSaveAndNew];
        self.shouldSwallowThisReturn = YES;
        [self.btnSaveAndNew setKeyEquivalent:@"\r"];
        break;
    case kNavModeSaveAndNext:
        [self.window makeFirstResponder:self.btnSaveAndNext];
        self.shouldSwallowThisReturn = YES;
        [self.btnSaveAndNext setKeyEquivalent:@"\r"];
        break;
    default:
        break;
}
我在所选按钮的操作中添加了几行:

if (self.shouldSwallowThisReturn) {
    self.shouldSwallowThisReturn = NO;
    return;
}
[self.btnSaveAndNext setKeyEquivalent:@""];
因此,按钮的其余操作仅在用户第二次按下return键后执行

这是可行的,但我更喜欢更优雅的解决方案

对Apple事件处理指南的进一步研究表明了问题所在:显然,当您使用IB将发送的操作分配给文本字段时,虽然该操作在用户按下return时启动,但该return不会注册为等效键,因此不会对应用程序的performKeyEquivalent查询响应yes,因此,该应用程序一直在寻找一个响应“是”的控件,因此它最终会自己调用该按钮

因此,我真正应该做的是将textfield子类化并重写其performKeyEquivalent方法,以便在keyCode为36(返回键的代码)时返回yes,如下所示:

- (BOOL) performKeyEquivalent:(NSEvent *)theEvent {
    printf("\nThe keycode is %d", [theEvent keyCode]);
    if ([theEvent keyCode] == 36) 
        return YES;
    else 
        return NO;
}
但发生的情况是,即使目标textfield没有焦点,也会调用override方法。事实上,即使选择的按钮已经是第一响应者,它也会被调用。所以现在用户的返回总是被抢占,按钮的动作永远不会被调用

我修改了覆盖方法以检查第一响应者的身份:

[self.btnSaveAndNext setKeyEquivalent:@""];
- (BOOL) performKeyEquivalent:(NSEvent *)theEvent {
    printf("\nThe keycode is %d", [theEvent keyCode]);
    if ([theEvent keyCode] == 36) {
        ThisProject_AppDelegate *appDelegate = [[NSApplication sharedApplication] delegate];
        id firstResponder = [appDelegate.windowController.window firstResponder];
        if ([firstResponder isKindOfClass:[NSTextView class]]) {
            printf("\nfirstResp is a field editor, a textview.");
            if ([firstResponder delegate] == self) {
                printf("\ntarget textfield is firstResponder.");
                return YES;
            }
        }
        else if ([firstResponder isKindOfClass:[NSButton class]]) {
            printf("\nfirstResp is a button.");
            return YES;
        }
    }
    return NO;
}
事实证明,当第一响应者的状态已经转移到按钮时,在执行textfield的sent操作后,会调用覆盖。因此,覆盖没有帮助


现在,我被困在这个答案的顶部。但是必须有某种方法来获取发送的操作,以完全捕获触发它的返回键事件…

StackOverflow小妖精建议我结束这个问题。我将这个答案标记为已接受,因为事实上BOOL应该接受这个返回的乱码工作得很好,并且给了我很多控制。我也发现了一个问题——结果是更改IB inspector调色板中的“send action on”菜单项没有任何效果。我必须手动调用
[self.searchTextField.cell setSendsActionOneDediting:NO]
以避免两次执行操作。谢谢。这可能是一个更好的解决方案,比我公认的答案含糊不清。