2009-03-04 80 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