8

我的应用程序有一个NSOutlineView和一个NSTableView,我对两者都有同样的问题。选中任一行后,按 Tab 键会将第一列置于编辑模式,而不是让下一个键查看第一响应者。要进入下一个关键视图,您需要在所有列之间切换。

此外,切换到任一视图会导致最后一列进入编辑模式,需要更多的切换选项卡才能进入其先前的关键视图。

万一这很重要,我使用的是自动计算的键视图循环,而不是我自己的,我NSWindow设置为autorecalculatesKeyViewLoop = YES. 一旦用户选择编辑列,我想在列之间切换,但我不认为 Tab 键触发编辑模式是标准行为。

更新

感谢以下有用的回复,我解决了。基本上,我-keyDown在我的自定义表格视图类中进行了覆盖,该类处理表格视图中的制表符和移位制表符。然而,解决表格视图中的 shift-tabbing 问题更加困难。如果它接受来自另一个视图的控制,我YES在自定义表视图中设置一个布尔属性。-acceptsFirstResponder

当当前事件是 shift-tab 事件时,委托会-tableView:shouldEditTableColumn:row进行检查keyDown-tableView:shouldEditTableColumn:row被调用并且它不是一个 shift-tab 事件,它将表视图的属性设置回,NO因此它仍然可以像往常一样进行编辑。

我在下面粘贴了完整的解决方案。

/* CustomTableView.h */

@interface CustomTableView : NSTableView {}

@property (assign) BOOL justFocused;

@end

/* CustomTableView.m */

@implementation CustomTableView

@synthesize justFocused;

- (BOOL)acceptsFirstResponder {
    if ([[self window] firstResponder] != self) {
        justFocused = YES;
    }

    return YES;
}

- (void)keyDown:(NSEvent *)theEvent
{
    // Handle the Tab key
    if ([[theEvent characters] characterAtIndex:0] == NSTabCharacter) {
        if (([theEvent modifierFlags] & NSShiftKeyMask) != NSShiftKeyMask) {
            [[self window] selectKeyViewFollowingView:self];
        } else {
            [[self window] selectKeyViewPrecedingView:self];
        }
    }
    else {
        [super keyDown:theEvent];
    }
}

@end

/* TableViewDelegate.m */

. . .

- (BOOL)tableView:(NSTableView *)tableView shouldEditTableColumn:(NSTableColumn *)tableColumn
              row:(NSInteger)row
{
    NSEvent *event = [NSApp currentEvent];
    BOOL shiftTabbedIn = ([event type] == NSKeyDown
                          && [[event characters] characterAtIndex:0] == NSBackTabCharacter);

    if (shiftTabbedIn && ((CustomTableView *)tableView).justFocused == YES) {
        return NO;
    } else {
        ((CustomTableView *)tableView).justFocused = NO;
    }

    return YES;
}

. . .
4

5 回答 5

9

这是默认行为。如果没有选择行,则整个表格视图具有焦点,并且 Tab 键切换到下一个键视图。如果选择了一行,则表格视图开始编辑,如果已经在编辑,则移动到下一个单元格。

来自AppKit 发行说明

表格现在支持单元间导航,如下所示:

  • 向前跳到一个表格会聚焦整个表格。
  • 如果该行中只有一个实例,则点击 Space 将尝试在所选行中的 NSButtonCell 上“执行单击:”。
  • 再次使用 Tab 键聚焦第一个“可聚焦”(1) 单元格(如果有的话)。
  • 如果可以编辑新聚焦的单元格,则将开始编辑。
  • Hitting Space 在单元格上调用“performClick:”,然后设置数据源值(如果更改)。(2)
  • 如果文本单元格正在编辑,按 Enter 将提交编辑,焦点将返回到 tableview,Tab/Shift-tab 将提交编辑,然后执行新的选项卡循环行为。
  • 选项卡只会通过单行选项卡
  • 到达一行中的最后一个单元格后,选项卡会将焦点转移到下一个可聚焦控件。
  • 返回到表格中的选项卡将选择最后一个可聚焦的单元格。

如果您想更改此行为,委托方法tableView:shouldEditTableColumn:row:可能会有所帮助。NSTableView如果您真的只想影响 Tab 键的行为,您可能还必须进行子类化。

于 2011-04-08T21:50:02.213 回答
2

我以前也不得不处理这个问题。我的解决方案是子类化NSTableViewNSOutlineView覆盖keyDown:以捕获那里的制表键按下,然后对它们采取行动。

于 2011-04-08T23:08:55.180 回答
2

使用的解决方案keyDown对我不起作用。也许是因为它是基于单元格的表格视图。

我在 Swift 中的基于视图的表格视图的解决方案如下所示:

extension MyTableView: NSTextFieldDelegate {
    func controlTextDidEndEditing(_ obj: Notification) {
        guard
            let view = obj.object as? NSView,
            let textMovementInt = obj.userInfo?["NSTextMovement"] as? Int,
            let textMovement = NSTextMovement(rawValue: textMovementInt) else { return }

        let columnIndex = column(for: view)
        let rowIndex = row(for: view)

        let newRowIndex: Int
        switch textMovement {
        case .tab:
            newRowIndex = rowIndex + 1
            if newRowIndex >= numberOfRows { return }
        case .backtab:
            newRowIndex = rowIndex - 1
            if newRowIndex < 0 { return }
        default: return
        }

        DispatchQueue.main.async {
            self.editColumn(columnIndex, row: newRowIndex, with: nil, select: true)
        }
    }
}

您还需要设置cell.textField.delegate以便实现工作。

我关于这个棘手的解决方法的博客文章:https ://samwize.com/2018/11/13/how-to-tab-to-next-row-in-nstableview-view-based-solution/

于 2018-11-13T08:16:09.557 回答
0

多么方便!我昨天自己也在看这个,很高兴看到我所采取的方法得到了一些确认——keyDown:处理。

但是,我对您的方法有一个小的可能改进:我发现触发编辑的方法是becomeFirstResponder调用。所以我对 NSTableView 子类所做的是:

  1. 添加合成属性以控制是否禁用选项卡编辑行为
  2. 在 keydown 上,检查选项卡的第一个字符(也检查[[theEvent characters] length]以避免死键的异常!);如果选项卡编辑被禁用,请根据您的代码示例转到下一个/上一个视图。
  3. 覆盖becomeFirstResponder
    - (BOOL) 成为第一响应者 {
        if (tabEditingDisabled) {
            【自我展示】;
            返回是;
        }
        返回[超级成为FirstResponder];
    }

这将所有代码保留在 tableview 子类中,保持委托更清洁 :)

唯一的危险是我不知道 NSTableView 在 becomeFirstResponder 中还有什么作用;我没发现有什么破东西,但是...

于 2011-04-10T15:32:19.240 回答
0

这对我有用:

- (BOOL)tableView:(NSTableView *)tableView shouldEditTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
  NSEvent *e = [NSApp currentEvent];
  if (e.type == NSKeyDown && e.keyCode == 48) return NO;
  return YES;
}
于 2013-01-06T19:22:13.817 回答