2009-03-04 78 views
9

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

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

回答

4

好吧,这是不容易的,但我设法做到了,而不必使用RRSpreadSheet或甚至另一个控件。以下是你需要做的:

  1. 创建一个NSTextView的子类,这将是字段编辑器。在这个例子中,名称MyFieldEditorClass将被使用,并且myFieldEditor将引用该类的一个实例。

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

  3. 添加另一个名为“setTableView:”的方法,并让它将NSTableView对象保存到某个位置,或者除非有另一种方式从字段编辑器中获取NSTableView对象,否则使用该方法。

  4. 添加另一种称为- (void) keyDown:(NSEvent *) event的方法。这实际上覆盖了NSResponderkeyDown:。源代码应为(注意,StackOverflow上的降价正在改变<>&lt;&gt;):

    - (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; 
    } 
    
  5. 给这个NSTableView的在你的笔尖的委托,并在委托添加方法:

    - (BOOL) tableView:(NSTableView *)aTableView shouldEditColumn:(NSTableColumn *) aCol row:aRow 
    { 
        if ([aTableView isEqual:TheTableViewYouWantToChangeBehaviour]) 
         [myFieldEditor setLastKnownColumn:[[aTableView tableColumns] indexOfObject:aCol] andRow:aRow]; 
        return YES; 
    } 
    
  6. 最后,给表视图的主窗口的委托,并添加方法:

    - (id) windowWillReturnFieldEditor:(NSWindow *) aWindow toObject:(id) anObject 
    { 
        if ([anObject isEqual:TheTableViewYouWantToChangeBehaviour]) 
        { 
         if (!myFieldEditor) 
         { 
          myFieldEditor = [[MyFieldEditorClass alloc] init]; 
          [myFieldEditor setTableView:anObject]; 
         } 
         return myFieldEditor; 
        } 
        else 
        { 
         return nil; 
        } 
    } 
    

运行该程序,并给它去!

1

与其强迫NSTableView做某些事情不是为了设计,您可能需要考虑使用为此目的设计的东西。我有一个开源的电子表格控件,它可以做你需要的东西,或者你至少可以扩展它来做你需要的东西:MBTableGrid

+0

对MBTableGrid(和一个慷慨的许可证)的伟大工作,但是这比我所做的要多得多的代码来做我需要的。 +1为您投入MBTableGrid的努力。 – dreamlax 2009-03-06 09:34:30

8

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

长话短说,我们想到了(我们没有覆盖左/右箭头键,因为这些键用于在单元格内导航。我们使用选项卡进入左/右)

请注意,这仅仅是从续集临源代码片段,并不起作用的是

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

这看起来非常强大,但如果你更好地解释你在做什么/如何做,它会有很大的帮助。例如我试过这个,它从来没有捕捉到向上/向下箭头键 - 虽然它捕获用户击中单元格上的Return,并编辑该单元格? – Adam 2011-05-08 14:00:47

+0

这只适用于编辑单元格。当您单击NSTableView的单元格时,窗口字段编辑器将放置在单元格上方,您可以编辑文本。在字段编辑器中编辑文本时,上述消息将发送到表视图的委托。因为我们所使用的委托方法的文档[这里](http://developer.apple.com/library/mac/documentation/cocoa/reference/NSControlTextEditingDelegate_Protocol/Reference/Reference.html#//apple_ref/occ/intfm/NSControlTextEditingDelegate /控制:TextView的:doCommandBySelector :)。 – 2011-05-10 21:22:10

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