【问题标题】:NSTableView & NSOutlineView editing on tab keyNSTableView 和 NSOutlineView 在 tab 键上编辑
【发布时间】:2011-08-01 19:54:12
【问题描述】:

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

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

以防万一,我使用的是自动计算的键视图循环,而不是我自己的,我的 NSWindow 设置为 autorecalculatesKeyViewLoop = YES。一旦用户选择编辑列,我希望在列之间切换,但我不认为这是 trigger 编辑模式的 tab 键的标准行为。

更新

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

当当前事件是 shift-tab keyDown 事件时,代理的 -tableView:shouldEditTableColumn:row 会检查这一点。 -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;
}

. . .

【问题讨论】:

    标签: cocoa macos keyboard nstableview nsoutlineview


    【解决方案1】:

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

    来自AppKit Release Notes

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

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

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

    【讨论】:

    • 我在发布此问题之前尝试使用 tableView:shouldEditTableColumn:row:,但无法提出任何有效标准作为返回 NO 的基础
    • 是的,如果你真的只想改变 Tab 的行为而不改变其他键,那么,就像我说的,你可能不得不子类化。不过,它应该相当轻松——您只需要覆盖keyDown: 并检查Tab。您刚刚传递给[super keyDown:event] 的任何其他密钥。
    • 这是有道理的。另外,[event keyCode] == 48 是捕获 tab 键的最佳方法,还是这就是为什么 shift-tab 仍然是一个问题?
    • 您可以查看事件的modifierFlags,看看是否按下了Shift。我不认为有更好的方法,不。
    • 哦,我错过了你在下面的评论。
    【解决方案2】:

    我以前也不得不处理这个问题。我的解决方案是继承 NSTableViewNSOutlineView 并覆盖 keyDown: 以捕捉那里的 tab 键按下,然后对其进行操作。

    【讨论】:

    • 我在发布之前尝试过这个,但它没有解决 shift-tab 场景,因为NSTableView 没有收到keyDown: 事件。你是怎么处理的?另外,[theEvent keyCode] == 48 是捕获 Tab 键的最佳方式,还是我的问题?
    • @Dov 您应该能够获取事件字符并将其与NSTabCharacterNSBackTabCharacter 进行比较。
    【解决方案3】:

    使用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/

    【讨论】:

      【解决方案4】:

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

      但是,我对您的方法有一个可能的小改进:我发现触发在 shift-tabbing 回到表格时进行编辑的方法是 becomeFirstResponder 调用。所以我对 NSTableView 子类所做的是:

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

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

      唯一的危险是我不知道 NSTableView 在 becomeFirstResponder 中还有什么作用;我没有注意到任何损坏,但是...

      【讨论】:

      • 不过,在您的解决方案中,您何时更改tabEditingDisabled
      • @Dov 因为我的 NSTableView 子类在少数地方使用,所以它只是一个标志,可以根据使用的位置切换行为。在我希望禁用选项卡编辑的几个地方,我将awakeFromNib 中的视图控制器更改为setTabEditingDisabled:NOtabEditingDisabled 不像代码中的 justFocused 那样是跟踪标志,更像是一种行为控制切换。
      【解决方案5】:

      这对我有用:

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

      【讨论】:

        猜你喜欢
        • 2010-10-13
        • 2011-09-22
        • 1970-01-01
        • 1970-01-01
        • 2015-11-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-22
        相关资源
        最近更新 更多