Клавиши со стрелками с NSTableView - PullRequest
10 голосов
/ 05 марта 2009

Можно ли перемещаться по редактируемой ячейке NSTableView вокруг NSTableView с помощью клавиш со стрелками и ввода / табуляции? Например, я хочу, чтобы это больше походило на электронную таблицу.

Ожидается, что пользователи этого приложения отредактируют довольно много ячеек (но не все), и я думаю, что было бы легче сделать это, если бы им не нужно было дважды щелкать по каждой ячейке.

Ответы [ 4 ]

8 голосов
/ 21 апреля 2010

В Sequel Pro мы использовали другой (и, на мой взгляд, более простой) метод: мы реализовали control:textView:doCommandBySelector: в делегате TableView. Этот метод трудно найти - его можно найти в справочнике по протоколу 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;
    }
4 голосов
/ 06 марта 2009

Ну, это не так просто, но мне удалось сделать это без использования RRSpreadSheet или даже другого элемента управления. Вот что вам нужно сделать:

  1. Создайте подкласс NSTextView, это будет редактор полей. В этом примере будет использоваться имя MyFieldEditorClass, а myFieldEditor будет ссылаться на экземпляр этого класса.

  2. Добавьте в MyFieldEditorClass метод с именем "- (void) setLastKnownColumn:(unsigned)aCol andRow:(unsigned) aRow" или что-то подобное, и пусть он сохранит оба значения входного параметра где-нибудь.

  3. Добавьте еще один метод с именем "setTableView:" и попросите его сохранить объект NSTableView где-нибудь, или, если нет другого способа получить объект NSTableView из редактора полей, используйте его.

  4. Добавьте еще один метод с именем - (void) keyDown:(NSEvent *) event. Это на самом деле отменяет NSResponder keyDown:. Исходный код должен быть (имейте в виду, что MarkDown 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 голос
/ 16 мая 2010

Я хотел ответить на ответы здесь, но кнопка ответа, кажется, отсутствует, поэтому я вынужден доказать ответ, когда я действительно просто хочу задать вопрос об ответах.

В любом случае, я видел несколько ответов для переопределения события -keyDown табличного представления, в которых говорится о создании подкласса TableView, но согласно каждой книге Objective-C, которую я прочитал до сих пор, и нескольким обучающим видео Apple, вы следует очень редко, если когда-либо подкласс одного из основных классов. Фактически, каждый из них подчеркивает, что программисты на C увлечены подклассами, а это не так, как работает Objective-C; что в Objective-C все о помощниках и делегатах, а не о создании подклассов.

Итак, я должен просто игнорировать какие-либо ответы, которые говорят подклассу, поскольку это, кажется, находится в прямом противоречии с заповедями Objective-C?

--- Редактировать ---

Я нашел что-то, что работало без подкласса NSTableView. Хотя я перемещаю наследование на одну ступень вверх по цепочке от NSObject к NSResponder, я не полностью подклассифицирую NSTableView. Я просто добавляю возможность переопределить событие keyDown.

Я сделал класс, который использовал в качестве делегата, наследуемым от NSResponder вместо NSObject и установил nextResponder для этого класса в awakeFromNib. Затем я смог отловить нажатие клавиш с помощью события keydown. Я, конечно, подключил IBOutlet и установил делегата в Интерфейсном Разработчике.

Вот мой код с минимумом, необходимым для отображения захвата ключа:

Заголовочный файл

//  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
1 голос
/ 06 марта 2009

Вместо того, чтобы заставлять NSTableView делать что-то, для чего он не предназначен, вы можете захотеть взглянуть на использование чего-то, разработанного для этой цели. У меня есть элемент управления электронными таблицами с открытым исходным кодом, который может делать то, что вам нужно, или вы, по крайней мере, сможете расширить его, чтобы делать то, что вам нужно: MBTableGrid

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...