【问题标题】:NSTextField captures return key event twiceNSTextField 两次捕获返回键事件
【发布时间】:2011-07-02 09:58:38
【问题描述】:

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

文本字段的发送操作选择器包含以下代码:

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:@""];

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

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

【问题讨论】:

  • 我也发现了这个问题——结果发现更改 IB 检查器面板中的“发送操作”菜单项没有任何效果。我不得不手动调用[self.searchTextField.cell setSendsActionOnEndEditing:NO] 以避免我的操作两次。
  • 谢谢。这可能是比我接受的答案更好的解决方案。

标签: objective-c nstextfield keyboard-navigation


【解决方案1】:

嗯,我有一个杂牌。

我添加了一个布尔属性 shouldSwallowThisReturn。我在文本字段的发送操作选择器中添加了一行,将此布尔值设置为 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 将已发送操作分配给文本字段时,尽管该操作在用户按下返回时被触发,但该返回并未注册为一个等效键,因此不会对应用程序的 performKeyEquivalent 查询响应“是”,因此该应用程序一直在寻找会响应“是”的控件,因此它最终会自行调用该按钮。

所以看来我真正应该做的是子类化文本字段并覆盖其 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,它也会被调用。所以现在用户的返回总是被抢占并且按钮的动作永远不会被调用。

我修改了 override 方法来检查 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 状态已经转移到按钮时,执行文本字段的 send-action 之后调用了覆盖。所以覆盖没有帮助。

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

【讨论】:

  • StackOverflow 小妖精建议我结束这个问题。我将此答案标记为已接受,因为事实上 BOOL shouldSwallowThisReturn kludge 运行良好并且给了我很大的控制权。
猜你喜欢
  • 2015-06-29
  • 2015-11-16
  • 2011-08-21
  • 2017-04-02
  • 1970-01-01
  • 2013-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多