NSTableView & NSTableView Tab 键上的 NSOutlineView 编辑
我的应用程序有一个 NSOutlineView
和一个 NSTableView
,我对两者都遇到了同样的问题。选择任一行后,按 Tab 键会将第一列置于编辑模式,而不是使下一个键查看第一响应者。要进入下一个关键视图,您需要按 Tab 键浏览所有列。
此外,按 Shift 键进入任一视图都会导致最后一列进入编辑模式,因此需要更多的 Shift 键才能进入其上一个关键视图。
如果重要的话,我正在使用自动计算的关键视图循环,而不是我自己的,并将我的 NSWindow
设置为 autorecalculatesKeyViewLoop = YES
。一旦用户选择编辑列,我希望在列之间进行 Tab 键切换,但我不认为 Tab 键触发编辑模式是标准行为。
更新
感谢下面的有用回复,我解决了。基本上,我在自定义表视图类中重写 -keyDown
,该类处理表视图中的 Tab 键切换和Shift-Tab 键切换。然而,解决将 Shift-Tab 键切换到表视图的问题却更加困难。如果自定义表视图的 -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;
}
. . .
My app has an NSOutlineView
and an NSTableView
, and I'm having the same problem with both. With a row in either selected, pressing the tab key puts the first column into edit mode instead of making the next key view first responder. To get to the next key view, you need to tab through all of the columns.
Also, shift-tabbing into either view results in the last column going into edit mode, necessitating more shift-tabs to get into its previous key view.
In case it matters, I'm using the autocalculated key view loop, not my own, with my NSWindow
set to autorecalculatesKeyViewLoop = YES
. I would like tabbing between the columns once the user elects to edit a column, but I don't think it's standard behavior for the tab key to trigger edit mode.
Update
Thanks to the helpful responses below, I worked it out. Basically, I override -keyDown
in my custom table view class, which handles tabbing and shift-tabbing out of the table view. It was tougher to solve shift-tabbing into the table view, however. I set a boolean property to YES
in the custom table view's -acceptsFirstResponder
if it's accepting control from another view.
The delegate's -tableView:shouldEditTableColumn:row
checks for that when the current event is a shift-tab keyDown
event. -tableView:shouldEditTableColumn:row
is called and it's not a shift-tab event, it sets the table view's property back to NO
so it can still be edited as usual.
I've pasted the full solution below.
/* 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;
}
. . .
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
这是默认行为。如果没有选择任何行,则整个表视图将具有焦点,并且 Tab 键会切换到下一个键视图。如果选择了一行,表格视图将开始编辑或移动到下一个单元格(如果已经在编辑)。
来自 AppKit 发行说明:
如果您想更改此行为,委托方法
tableView:shouldEditTableColumn:row:
可能会有所帮助。如果您确实只想影响 Tab 键的行为,您可能还必须对NSTableView
进行子类化。This is the default behavior. If there's no row selected, the table view as a whole has focus, and the Tab key switches to the next key view. If there is a row selected, the table view begins editing or moves to the next cell if already editing.
From AppKit Release Notes:
If you want to change this behavior, the delegate method
tableView:shouldEditTableColumn:row:
may be helpful. You may also have to subclassNSTableView
if you really want to affect only the behavior of the Tab key.使用
keyDown
的解决方案对我不起作用。也许是因为它是基于单元格的表格视图。我在 Swift 中针对基于视图的表格视图的解决方案如下所示:
您还需要设置
cell.textField.delegate
以便实现能够正常工作。我关于这个棘手的解决方法的博客文章: https://samwize.com/2018/11/13/how-to-tab-to-next-row-in-nstableview-view-based-solution/
The solution using
keyDown
didn't work for me. Perhaps because it is for cell-based table view.My solution for a view-based table view, in Swift, looks like this:
You also need to set the
cell.textField.delegate
so that the implementation works.My blog post on this tricky workaround: https://samwize.com/2018/11/13/how-to-tab-to-next-row-in-nstableview-view-based-solution/
我以前也必须处理过这个问题。我的解决方案是子类
NSTableView
或NSOutlineView
并重写keyDown:
以捕获那里的 Tab 键按下,然后对其进行操作。I've had to deal with this before as well. My solution was to subclass
NSTableView
orNSOutlineView
and overridekeyDown:
to catch the tab key presses there, then act on them.多么方便啊!昨天我自己刚刚看到了这个,很高兴看到我所采取的方法的一些确认 -
keyDown:
处理。不过,我对您的方法有一个可能的小改进:我发现,在按 Shift-Tab 键返回表格时触发编辑的方法是
becomeFirstResponder
调用。所以我在 NSTableView 子类上所做的是:[[theEvent strings] length]
以避免死键例外!)用于制表符;如果选项卡编辑被禁用,请根据您的代码示例转到下一个/上一个视图。成为FirstResponder
:这将所有代码保留在 tableview 子类中,从而使委托保持干净:)
唯一的危险是我不知道 NSTableView 在becomeFirstResponder 中还做了什么;我没有注意到有什么损坏,但是...
How convenient! I was just looking at this myself yesterday, and it's good to see some confirmation of the approach I took -
keyDown:
handling.However, I have one small possible refinement to your approach: I worked out that the method triggering editing on shift-tabbing back to the table was the
becomeFirstResponder
call. So what I did on a NSTableView subclass was:[[theEvent characters] length]
to avoid exceptions for dead keys!) for tab; if tab editing is disabled, move on to the next/previous view, as per your code sample.becomeFirstResponder
:This keeps all the code in the tableview subclass, keeping the delegate cleaner :)
The only danger is I don't know what else NSTableView does in becomeFirstResponder; I didn't notice anything breaking, but...
这对我有用:
This worked for me: