2009-03-04 15 views
9

¿Es posible navegar por una celda editable de NSTableView alrededor de NSTableView usando las teclas de flecha e introducir/tab? Por ejemplo, quiero que se sienta más como una hoja de cálculo.Teclas de flecha con NSTableView

Se espera que los usuarios de esta aplicación editen bastantes células (pero no todas), y creo que sería más fácil hacerlo si no tuvieran que hacer doble clic en cada celda.

Respuesta

4

Bueno, no es fácil, pero me las arreglé para hacerlo sin tener que utilizar RRSpreadSheet o incluso otro control. Esto es lo que tiene que hacer:

  1. Crear una subclase de NSTextView, este será el editor de campo. Para este ejemplo, se usará el nombre MyFieldEditorClass y myFieldEditor se referirá a una instancia de esta clase.

  2. añadir un método para MyFieldEditorClass llamado "- (void) setLastKnownColumn:(unsigned)aCol andRow:(unsigned) aRow" o algo similar, y tienen que guardar tanto los valores de los parámetros de entrada en alguna parte.

  3. Agregue otro método llamado "setTableView:" y haga que guarde el objeto NSTableView en alguna parte, o a menos que haya otra forma de obtener el objeto NSTableView del editor de campo, úselo.

  4. Agregar otro método llamado - (void) keyDown:(NSEvent *) event. Esto realmente está anulando NSResponder 's keyDown:. El código fuente debe ser (tenga en cuenta que MarkDown de Stackoverflow está cambiando < y >-&lt; y &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. Dar la NSTableView en su plumín de un delegado, y en el delegado añadir el método:

    - (BOOL) tableView:(NSTableView *)aTableView shouldEditColumn:(NSTableColumn *) aCol row:aRow 
    { 
        if ([aTableView isEqual:TheTableViewYouWantToChangeBehaviour]) 
         [myFieldEditor setLastKnownColumn:[[aTableView tableColumns] indexOfObject:aCol] andRow:aRow]; 
        return YES; 
    } 
    
  6. Por último, dar a la ventana principal de la vista de tabla a un delegado y añadir el método:

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

Ejecute el programa y pruébelo!

1

En lugar de forzar NSTableView a hacer algo que no fue diseñado, es posible que desee buscar en el uso algo diseñado para este propósito. Tengo un control de hoja de cálculo de código abierto que puede hacer lo que necesita, o al menos puede ampliarlo para hacer lo que necesita: MBTableGrid

+0

Gran trabajo en MBTableGrid (y una licencia generosos también), pero eso es mucho más código que lo que he hecho para hacer lo que necesito sin embargo. +1 por el esfuerzo que ha puesto en MBTableGrid. – dreamlax

8

En Sequel Pro usamos uno diferente (y en mi opinión más simple)) método: implementamos control:textView:doCommandBySelector: en el delegado de TableView. Este método es difícil de encontrar: se puede encontrar en NSControlTextEditingDelegate Protocol Reference. (Recuerde que NSTableView es una subclase de NSControl)

Para resumir, esto es lo que se nos ocurrió (no anulamos las teclas de flecha izquierda/derecha, ya que se usan para navegar dentro de la celda.Utilizamos Tab para ir a la izquierda/derecha)

Tenga en cuenta que esto es sólo un fragmento del código fuente Sequel Pro, y no funciona como es

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

Esto parece realmente poderoso, pero sería de gran ayuda si explicaras un poco mejor lo que estás haciendo/cómo. p.ej. Intenté esto, y nunca captura las teclas de flecha arriba/abajo para mí, aunque captura al usuario presionando Retorno en una celda y edita esa celda. – Adam

+0

Esto solo funciona al editar una celda. Cuando hace clic en una celda de un NSTableView, el editor de campo de Windows se coloca encima de la celda y puede editar el texto. Mientras edita texto en el editor de campo, el mensaje anterior se envía al delegado de la vista de tabla. La documentación para el método delegado que utilizamos es [aquí] (http://developer.apple.com/library/mac/documentation/cocoa/reference/NSControlTextEditingDelegate_Protocol/Reference/Reference.html#//apple_ref/occ/intfm/NSControlTextEditingDelegate/control: textView: doCommandBySelector :). –

1

quería responder a las respuestas aquí, pero parece que falta el botón de respuesta, así que estoy obligado a probar una respuesta cuando realmente solo quiero hacer una pregunta sobre las respuestas.

De todos modos, he visto algunas respuestas para anular el evento -keyDown de la vista de tabla que dicen subclase el TableView pero de acuerdo con cada libro de Objective-C que he leído hasta ahora, y varios videos de entrenamiento de Apple, muy rara vez, si alguna vez subclases, una de las clases principales. De hecho, cada uno de ellos señala que los programadores C tienen una fascinación por las subclases y que no es así como funciona Objective-C; que Objective-C se trata de ayudantes y delegados que no hacen subclases.

Entonces, ¿debería simplemente ignorar cualquiera de las respuestas que dicen a la subclase ya que esto parece estar en contradicción directa con los preceptos de Objective-C?

--- --- Editar

encontré algo que funcionaba sin la subclasificación de la NSTableView. Si bien muevo la herencia a una muesca de la cadena desde NSObject a NSResponder, no estoy subclasificando totalmente el NSTableView. Solo agrego la capacidad de anular el evento KeyDown.

Hice que la clase que estaba usando como delegado herede de NSResponder en lugar de NSObject y configuré el nextResponder en esa clase en awakeFromNib. Pude atrapar pulsaciones de teclas usando el evento Keydown. Por supuesto, conecté IBOutlet y configuré el delegado en Interface Builder.

Aquí está mi código con el mínimo necesario para mostrar la captura de la clave:

Archivo de cabecera

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

Aquí está el archivo 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

Hay algunas clases que no debe subclasificar, pero hay muchas clases Core (es decir, Foundation y AppKit) que * debe * o * debe * subclase. 'NSObject' es el más obvio. – dreamlax

+0

He aprendido un poco más, todavía estoy muy lejos de ser un novato con Objective-C, pero ahora estoy más seguro de que mi solución fue la mejor. No es necesario hacer toda la clase NSTableView cuando solo necesito atrapar el evento de keydown.Por el poco conocimiento que tengo, esto parece encajar mejor con la visión de Apple sobre cómo se debe escribir el código en Objective-C y después de lidiar con más y más de las API de Apple, realmente estoy comenzando a respetar el diablo con sus estándares. ; especialmente después de venir de un mundo de Windows donde a menudo su propio código no seguía sus propios estándares. –

Cuestiones relacionadas