NSTableView 和 NSOutlineView 在 tab 键上编辑
Posted
技术标签:
【中文标题】NSTableView 和 NSOutlineView 在 tab 键上编辑【英文标题】:NSTableView & NSOutlineView editing on tab key 【发布时间】: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;
. . .
【问题讨论】:
【参考方案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】:
我以前也不得不处理这个问题。我的解决方案是继承 NSTableView
或 NSOutlineView
并覆盖 keyDown:
以捕捉那里的 tab 键按下,然后对其进行操作。
【讨论】:
我在发布之前尝试过这个,但它没有解决 shift-tab 场景,因为NSTableView
没有收到keyDown:
事件。你是怎么处理的?另外,[theEvent keyCode] == 48
是捕获 Tab 键的最佳方式,还是我的问题?
@Dov 您应该能够获取事件字符并将其与NSTabCharacter
或NSBackTabCharacter
进行比较。【参考方案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 子类所做的是:
-
添加合成属性以控制是否禁用选项卡编辑行为
在 keydown 时,检查第一个字符(同时检查
[[theEvent characters] length]
以避免死键异常!)用于制表符;如果选项卡编辑被禁用,请根据您的代码示例转到下一个/上一个视图。
覆盖becomeFirstResponder
:
- (BOOL)becomeFirstResponder if (tabEditingDisabled) 【自我展示】; 返回是; 返回[超级成为FirstResponder];
这将所有代码保留在 tableview 子类中,保持委托更清洁 :)
唯一的危险是我不知道 NSTableView 在 becomeFirstResponder 中还有什么作用;我没有注意到任何损坏,但是...
【讨论】:
不过,在您的解决方案中,您何时更改tabEditingDisabled
?
@Dov 因为我的 NSTableView 子类在少数地方使用,所以它只是一个标志,可以根据使用的位置切换行为。在我希望禁用选项卡编辑的几个地方,我将awakeFromNib
中的视图控制器更改为setTabEditingDisabled:NO
。 tabEditingDisabled
不像代码中的 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;
【讨论】:
以上是关于NSTableView 和 NSOutlineView 在 tab 键上编辑的主要内容,如果未能解决你的问题,请参考以下文章
使用乘法 NSArrayControllers 和 NSTableView 绑定数据
NSTableView 和 NSOutlineView 在 tab 键上编辑