带有NSTableView的箭头键

时间:2009-03-04 22:43:31

标签: objective-c cocoa macos

是否可以使用箭头键并输入/ tab在NSTableView周围导航NSTableView的可编辑单元格?例如,我想让它感觉更像电子表格。

此应用程序的用户需要编辑相当多的单元格(但不是全部),我认为如果他们不必双击每个单元格,这样做会更容易。< / p>

4 个答案:

答案 0 :(得分:8)

在Sequel Pro中,我们使用了一种不同的(在我看来更简单)方法:我们在TableView的委托中实现了control:textView:doCommandBySelector:。这种方法很难找到 - 可以在 NSControlTextEditingDelegate协议参考中找到。 (请记住,NSTableView是NSControl的子类)

长话短说,这就是我们想出的结果(我们没有覆盖左/右箭头键,因为它们用于在单元格内导航。我们使用Tab左/右)

请注意,这只是Sequel Pro源代码的一小部分,并不能正常工作

- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)command
{
    NSUInteger row, column;

    row = [tableView editedRow];
    column = [tableView editedColumn];

    // Trap down arrow key
    if (  [textView methodForSelector:command] == [textView methodForSelector:@selector(moveDown:)] )
    {
        NSUInteger newRow = row+1;
        if (newRow>=numRows) return TRUE; //check if we're already at the end of the list
        if (column>= numColumns) return TRUE; //the column count could change

        [tableContentView selectRowIndexes:[NSIndexSet indexSetWithIndex:newRow] byExtendingSelection:NO];
        [tableContentView editColumn:column row:newRow withEvent:nil select:YES];
        return TRUE;
    }

    // Trap up arrow key
    else if (  [textView methodForSelector:command] == [textView methodForSelector:@selector(moveUp:)] )
    {
        if (row==0) return TRUE; //already at the beginning of the list
        NSUInteger newRow = row-1;

        if (newRow>=numRows) return TRUE;
        if (column>= numColumns) return TRUE;

        [tableContentView selectRowIndexes:[NSIndexSet indexSetWithIndex:newRow] byExtendingSelection:NO];
        [tableContentView editColumn:column row:newRow withEvent:nil select:YES];
        return TRUE;
    }

答案 1 :(得分:4)

嗯,这并不容易,但我设法做到这一点,而不必使用RRSpreadSheet甚至其他控件。这是你必须做的:

  1. 创建NSTextView的子类,这将是字段编辑器。对于此示例,将使用名称MyFieldEditorClassmyFieldEditor将引用此类的实例。

  2. MyFieldEditorClass添加名为“- (void) setLastKnownColumn:(unsigned)aCol andRow:(unsigned) aRow”的方法或类似的方法,并将其保存在某处的输入参数值。

  3. 添加另一个名为“setTableView:”的方法并将其保存在某处,或者除非有其他方法从字段编辑器中获取NSTableView对象,否则请使用它。

  4. 添加另一个名为- (void) keyDown:(NSEvent *) event的方法。这实际上覆盖了NSResponder的{​​{1}}。源代码应该是(请注意StackOverflow的MarkDown正在将keyDown:<更改为>&lt;):

    &gt;
  5. 在你的笔尖中给NSTableView一个委托,并在委托中添加方法:

    - (void) keyDown:(NSEvent *) event
    {
        unsigned newRow = row, newCol = column;
        switch ([event keyCode])
        {
            case 126: // Up
                if (row)
                newRow = row - 1;
                break;
    
            case 125: // Down
                if (row < [theTable numberOfRows] - 1)
                    newRow = row + 1;
                break;
    
            case 123: // Left
                if (column > 1)
                    newCol = column - 1;
                break;
    
            case 124: // Right
                if (column < [theTable numberOfColumns] - 1)
                    newCol = column + 1;
                break;
    
            default:
                [super keyDown:event];
                return;
        }
    
        [theTable selectRow:newRow byExtendingSelection:NO];
        [theTable editColumn:newCol row:newRow withEvent:nil select:YES];
        row = newRow;
        column = newCol;
    }
    
  6. 最后,给表视图的主窗口一个委托并添加方法:

    - (BOOL) tableView:(NSTableView *)aTableView shouldEditColumn:(NSTableColumn *) aCol row:aRow
    {
        if ([aTableView isEqual:TheTableViewYouWantToChangeBehaviour])
            [myFieldEditor setLastKnownColumn:[[aTableView tableColumns] indexOfObject:aCol] andRow:aRow];
        return YES;
    }
    
  7. 运行程序然后试一试!

答案 2 :(得分:1)

您可能希望使用为此目的设计的内容,而不是强迫NSTableView执行其未设计的内容。我有一个开源电子表格控件可以做你需要的,或者你至少可以扩展它来做你需要的:MBTableGrid

答案 3 :(得分:1)

我想在这里回复答案,但回复按钮似乎不见了,所以当我真的只是想问一些关于回复的问题时,我被迫证明了答案。

无论如何,我已经看到了一些覆盖表视图的-keyDown事件的答案,这些事件说明了TableView的子类,但根据我到目前为止读过的每本Objective-C书,以及几个Apple培训视频,你应该很少,如果它是一个核心类的子类。事实上,它们中的每一个都表明C程序员对子类化有着浓厚的兴趣,而不是Objective-C的工作原理; Objective-C是关于助手和委托而不是子类化的。

那么,我是否应该忽略对子类说的任何响应,因为这似乎与Objective-C的规则直接矛盾?

---编辑---

我发现了一些没有子类化NSTableView的东西。虽然我确实将链接上的继承从NSObject移动到NSResponder,但我并没有完全继承NSTableView。我只是添加了覆盖keyDown事件的能力。

我使用了作为委托继承自NSResponder而不是NSObject的类,并将nextResponder设置为awakeFromNib中的该类。然后,我可以使用keydown事件捕获按键。我当然连接了IBOutlet并在Interface Builder中设置了委托。

这是我的代码,其中显示了密钥的捕获所需的最小代码:

标头文件

//  AppController.h

#import <Cocoa/Cocoa.h>

@interface AppController : NSResponder {

    IBOutlet NSTableView *toDoListView;
    NSMutableArray *toDoArray;
}

-(int)numberOfRowsInTableView:(NSTableView *)aTableView;

-(id)tableView:(NSTableView *)tableView
objectValueForTableColumn:(NSTableColumn *)aTableColumn
           row:(int)rowIndex;

@end

这是m文件。

//  AppController.m
#import "AppController.h"

@implementation AppController

-(id)init
{
    [super init];
    toDoArray = [[NSMutableArray alloc] init];
    return self;
}

-(void)dealloc
{
    [toDoArray release];
    toDoArray = nil;
    [super dealloc];
}

-(void)awakeFromNib
{
    [toDoListView setNextResponder:self];
}

-(int)numberOfRowsInTableView:(NSTableView *)aTableView
{
    return [toDoArray count];
}

-(id)tableView:(NSTableView *)tableView
    objectValueForTableColumn:(NSTableColumn *)aTableColumn
                          row:(int)rowIndex
{
    NSString *value = [toDoArray objectAtIndex:rowIndex];
    return value;
}

- (void)keyDown:(NSEvent *)theEvent
{
    //NSLog(@"key pressed: %@", theEvent);
    if (theEvent.keyCode == 51 || theEvent.keyCode == 117)
    {
        [toDoArray removeObjectAtIndex:[toDoListView selectedRow]];
        [toDoListView reloadData];
    }
}
@end