2009-03-04 6 views
9

Czy można nawigować w edytowalnej komórce NSTableView wokół NSTableView za pomocą klawiszy strzałek i enter/tab? Na przykład chcę sprawić, aby był bardziej podobny do arkusza kalkulacyjnego.Klawisze strzałek z NSTableView

Użytkownicy tej aplikacji powinni edytować dość dużo komórek (ale nie wszystkie) i myślę, że byłoby to łatwiejsze, gdyby nie musieli klikać podwójnie na każdą komórkę.

Odpowiedz

4

Cóż, nie jest to łatwe, ale udało mi się to zrobić bez konieczności używania RRSpreadSheet lub nawet innej kontroli. Oto co trzeba zrobić:

  1. utworzyć podklasę NSTextView, będzie to edytor pole. W tym przykładzie zostanie użyta nazwa MyFieldEditorClass, a myFieldEditor będzie odwoływać się do instancji tej klasy.

  2. Dodaj metodę do MyFieldEditorClass o nazwie "- (void) setLastKnownColumn:(unsigned)aCol andRow:(unsigned) aRow" lub coś podobnego i zapisz w niej gdziekolwiek wartości parametrów wejściowych.

  3. Dodaj inną metodę o nazwie „setTableView:” i to zapisać obiekt NSTableView gdzieś, lub jeżeli nie ma innego sposobu, aby uzyskać obiekt NSTableView z edytora pola, użyj tego.

  4. Dodaj inną metodę o nazwie - (void) keyDown:(NSEvent *) event. Jest to w rzeczywistości przesłonięcie NSResponder 's keyDown:. Kod źródłowy powinien być (należy pamiętać, że Przecena StackOverflow się zmienia < i > do &lt; i &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. Daj NSTableView w swojej stalówka delegata, a delegatem dodać metodę:

    - (BOOL) tableView:(NSTableView *)aTableView shouldEditColumn:(NSTableColumn *) aCol row:aRow 
    { 
        if ([aTableView isEqual:TheTableViewYouWantToChangeBehaviour]) 
         [myFieldEditor setLastKnownColumn:[[aTableView tableColumns] indexOfObject:aCol] andRow:aRow]; 
        return YES; 
    } 
    
  6. Wreszcie dać głównym oknie tabeli widok na delegata i dodać metodę:

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

Uruchom program i daj mu szansę!

1

Zamiast zmuszać NSTableView do zrobienia czegoś, do czego nie był przeznaczony, warto przyjrzeć się użyciu czegoś zaprojektowanego do tego celu. Mam kontrolę arkusza kalkulacyjnego open source, która może zrobić to, czego potrzebujesz, lub możesz co najmniej być w stanie rozszerzyć ją, aby zrobić to, czego potrzebujesz: MBTableGrid

+0

Świetna praca na MBTableGrid (i hojnej licencji), ale to znacznie więcej kodu niż to, co zrobiłem, aby zrobić to, czego potrzebuję. +1 za wysiłek wkładany w MBTableGrid. – dreamlax

8

W Sequel Pro użyliśmy innego (i na moje oczy prostsze)): Zaimplementowaliśmy control:textView:doCommandBySelector: w delegata TableView. Ta metoda jest trudna do znalezienia - można ją znaleźć w NSControlTextEditingDelegate Protocol Reference. (Pamiętaj, że NSTableView jest podklasą NSControl)

Krótko mówiąc, oto, co wymyśliliśmy (nie zastąpiliśmy klawiszy strzałek w lewo/w prawo, ponieważ służą one do poruszania się w obrębie komórki.Używamy Tab, aby przejść w lewo/w prawo)

Należy pamiętać, że jest to tylko fragment z kodu Sequel Pro źródłowego, a nie działa jak

- (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

To wygląda naprawdę potężnie, ale bardzo by pomogło, gdybyś wyjaśnił nieco lepiej, co robisz/jak. na przykład Próbowałem tego i nigdy nie przechwytywałem klawiszy strzałek w górę/w dół dla mnie - chociaż robi to przechwycenie użytkownika uderzającego Return w komórkę i edytowanie tej komórki? – Adam

+0

Działa to tylko podczas edytowania komórki. Po kliknięciu w komórkę NSTableView edytor pól okien znajduje się nad komórką i można edytować tekst. Podczas edytowania tekstu w edytorze pól, powyższa wiadomość jest wysyłana do delegowania widoku tabeli. Dokumentacja dla metody delegatów, której używaliśmy, znajduje się tutaj (http://developer.apple.com/library/mac/documentation/cocoa/reference/NSControlTextEditingDelegate_Protocol/Reference/Reference.html#//apple_ref/occ/intfm/NSControlTextEditingDelegate/control: textView: doCommandBySelector :). –

1

Chciałem odpowiedzieć odpowiedzi tutaj, ale przycisk odpowiedzi wydaje się być nieobecny, więc jestem zmuszony udowodnić odpowiedź, kiedy naprawdę chcę zadać pytanie o odpowiedzi.

W każdym razie, widziałem kilka odpowiedzi na przesłonięcie zdarzenia -keyDown widoku tabeli, które mówi do podklasy TableView, ale zgodnie z każdą książką Objective-C, którą przeczytałem do tej pory, oraz kilka filmów szkoleniowych firmy Apple, powinieneś bardzo rzadko, jeśli kiedykolwiek, podklasuj jedną z podstawowych klas. W rzeczywistości każdy z nich wskazuje na to, że programiści C mają fascynację podklasowaniem i nie jest tak, jak działa Objective-C; w Objective-C chodzi o pomocników, a delegaci nie podklasy.

Więc, czy powinienem po prostu zignorować każdą odpowiedź, która mówi do podklasy, ponieważ wydaje się to być w bezpośredniej sprzeczności z przykazaniami Celu C?

--- Edit ---

znalazłem coś, co działało bez instacji NSTableView. Podczas gdy przenoszę dziedziczenie o jeden poziom w łańcuchu z NSObject na NSResponder, nie jestem całkowicie podklasy NSTableView. Dodaję możliwość nadpisania zdarzenia keyDown.

Ustawiłem klasę, której używałem jako delegata, dziedziczę z obiektu NSResponder zamiast NSObject i ustaw następnąResponder na tę klasę w awakeFromNib. Byłem wtedy w stanie pułapkować naciśnięcia klawiszy za pomocą zdarzenia keydown. Oczywiście podłączyłem IBOutlet i ustawiłem delegata w Interface Builder.

Oto mój kod z minimum niezbędnego, aby pokazać odłowu klucza:

plik nagłówka

// 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 

Oto plik 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 
+0

Jest kilka klas, które nie powinny być podklasą, ale istnieje wiele podstawowych (tj. Podstawowa i AppKit) klas, które * powinna * lub * musi * podklasa. 'NSObject' jest najbardziej oczywistym. – dreamlax

+0

Dowiedziałem się trochę więcej, wciąż bardzo daleko od bycia nowicjuszem w Objective-C, ale teraz jestem już bardziej pewien, że moje rozwiązanie jest lepsze. Nie ma potrzeby wykonywania całej klasy NSTableView, gdy potrzebuję tylko pułapki na zdarzenie keydown.Z niewielkiej ilości wiedzy mam to wydaje się lepiej pasować do wizji Apple'a, jak kod powinien być napisany w Objective-C, a kiedy mam do czynienia z coraz większą ilością API Apple'a, naprawdę zaczynam szanować ich standardy ; zwłaszcza po pochodzeniu ze świata Windows, gdzie często ich własny kod nie spełniał własnych standardów. –