NSTableView&在Tab键上编辑NSOutlineView

时间:2011-04-08 21:08:13

标签: cocoa macos keyboard nstableview nsoutlineview

我的应用有一个NSOutlineView和一个NSTableView,我遇到了同样的问题。如果选择了一行,则按Tab键会将第一列置于编辑模式,而不是使下一个键视图成为第一响应者。要进入下一个关键视图,您需要标记所有列。

此外,将Shift-tabbing转换为任一视图会导致 last 列进入编辑模式,从而需要更多shift-tabs进入其先前的关键视图。

如果重要,我使用自动计算的关键视图循环,而不是我自己的,我的NSWindow设置为autorecalculatesKeyViewLoop = YES。一旦用户选择编辑列,我希望在列之间进行选项卡,但我不认为选项卡键的标准行为是触发编辑模式。

更新

感谢下面的有用回复,我解决了这个问题。基本上,我在自定义表视图类中重写-keyDown,该类处理表格视图中的Tab键和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;
}

. . .

5 个答案:

答案 0 :(得分:7)

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

来自AppKit Release Notes

  

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

     
      
  • 向前跳转到表格会聚焦整个表格。
  •   
  • Hitting Space将尝试在NSButtonCell上执行'performClick:'   选中的行,如果只有一行   该行中的实例。
  •   
  • Tabbing会再次聚焦第一个“可聚焦”(1)单元格(如果有)。
  •   
  • 如果可以编辑新聚焦的单元格,则将开始编辑。
  •   
  • 命中空间在单元格上调用'performClick:'并设置数据源   之后的价值,如果改变了。 (2)
  •   
  • 如果正在编辑文本单元格,按Enter键将提交编辑和焦点   将返回到tableview,并且   Tab / Shift-tab将提交编辑   然后执行新的制表循环   行为。
  •   
  • Tabbing只会标记一行
  •   
  • 一旦到达连续的最后一个单元格,选项卡将关注焦点   下一个可控制的控制。
  •   
  • 返回表格将选择最后一个可聚焦单元格。
  •   

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

答案 1 :(得分:2)

我之前也必须处理这个问题。我的解决方案是继承NSTableViewNSOutlineView并覆盖keyDown:以抓住Tab键,然后按下它们。

答案 2 :(得分:0)

多么方便!我昨天只是在看这个问题,很高兴看到我采取的方法有一些确认 - keyDown:处理。

但是,我对你的方法有一个小小的改进:我得出结论,在转换表上触发编辑的方法是becomeFirstResponder调用。所以我在NSTableView子类上所做的是:

  1. 添加合成属性以控制是否禁用制表符编辑行为
  2. 在keydown上,检查第一个字符(也检查[[theEvent characters] length]以避免死键的例外!)。如果禁用了选项卡编辑,请根据您的代码示例继续查看下一个/上一个视图。
  3. 覆盖becomeFirstResponder
    - (BOOL)becomeFirstResponder {
        if (tabEditingDisabled) {
            [self display];
            return YES;
        }
        return [super becomeFirstResponder];
    }

这会保留tableview子类中的所有代码,使委托更清晰:)

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

答案 3 :(得分: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;
}

答案 4 :(得分:0)

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