NSTextField两次捕获返回键事件

时间:2011-02-23 00:02:16

标签: objective-c nstextfield keyboard-navigation

我有一个文本字段和两个按钮。用户应该能够在完成编辑文本字段后按返回,然后再次返回以激活一个或另一个按钮,具体取决于条件。为了向用户说明他们可以返回以激活按钮,我暂时将返回指定为所选按钮的等效键,这将使其呈蓝色亮起。

textfield的sent-action选择器包含以下代码:

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;
}

所选按钮的动作然后敲出等效键,这样一旦它再次响应,按钮就不会继续呈蓝色亮起:

[self.btnSaveAndNext setKeyEquivalent:@""];

问题是,当用户返回文本字段时,返回键事件以某种方式被捕获两次,并且程序自己激活该按钮,即使用户实际上没有再次按下返回。

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

1 个答案:

答案 0 :(得分:1)

好吧,我有一个kludge。

我添加了一个布尔属性shouldSwallowThisReturn。我在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:@""];

因此,只有在用户再次按下返回后,按钮操作的其余部分才会执行。

这很有效,但我更喜欢更优雅的解决方案。

对Apple的事件处理指南的进一步检查表明出现了什么问题:显然,当您使用IB将发送动作分配给文本字段时,虽然当用户按下返回时操作被设置,但该返回不会注册为一个等效键,因此不会对应用程序的performKeyEquivalent查询作出响应,因此应用程序会一直寻找一个会响应是的控件,因此它最终会自行调用该按钮。

所以我真正应该做的是对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;
}

但是,即使目标文本字段没有焦点,也会调用覆盖方法。实际上,即使所选按钮已经是第一个响应者,它也会被调用。所以现在用户的返回总是被抢占,并且永远不会调用按钮的动作。

我修改了覆盖方法以检查firstResponder的身份:

- (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;
}

事实证明,当firstResponder状态已经转移到按钮时,在执行文本字段的发送操作后,覆盖被称为。所以覆盖没有帮助。

就目前而言,我坚持在这个答案的顶部使用kludge。但必须有一些方法可以让发送动作完全捕获将其设置为关闭的返回键事件...