NSTableView + Delete Key
Asked Answered
D

4

11

I'm looking for an easy solution to delete NSTableView rows by pushing the delete key.

All I have seen when searching in Google were answers like this: http://likethought.com/lockfocus/2008/04/a-slightly-improved-nstableview/

This seems to me an Engineering solution, but I would like to know if this is the best way. Does any one know a better answer?

Dickman answered 12/1, 2011 at 12:35 Comment(0)
H
12

I've implemented something similar to LTKeyPressTableView. However, I use array controllers, so in my subclass I added IBOutlet NSArrayController * relatedArrayController. Instead of handling delete request in a delegate, I handle it directly in the subclass since my subclass specifically deals with adding handling of Delete key. When I detect keypress for delete key, I'm just calling [relatedArrayController delete:nil];.

IRTableView.h:

#import <Cocoa/Cocoa.h>

@interface IRTableView : NSTableView {
    IBOutlet NSArrayController * relatedArrayController;
}

@end

and IRTableView.m:

#import "IRTableView.h"


@implementation IRTableView


- (void)keyDown:(NSEvent *)event
{
    // Based on LTKeyPressTableView.
    //https://github.com/jacobx/thoughtkit/blob/master/LTKeyPressTableView

    id delegate = [self delegate];

    // (removed unused LTKeyPressTableView code)

    unichar key = [[event charactersIgnoringModifiers] characterAtIndex:0];
    if(key == NSDeleteCharacter)
    {
        if([self selectedRow] == -1)
        {
            NSBeep();
        }

        BOOL isEditing = ([[self.window firstResponder] isKindOfClass:[NSText class]] && 
                          [[[self.window firstResponder] delegate] isKindOfClass:[IRTableView class]]);
        if (!isEditing) 
        {
            [relatedArrayController remove:nil];
            return;
        }

    }

    // still here?
    [super keyDown:event];
}

@end

End result is quite IB-friendly for me, and a quite simple solution for use in a Cocoa Bindings+Core Data application.

Hypogene answered 26/9, 2011 at 13:57 Comment(0)
C
27

What I usually do is create a new menu item in your application's menu bar. Something like:

File -> Delete ${Name of Item}

Then you can link that NSMenuItem in Interface Builder to point to an IBAction method defined somewhere on either your app delegate or some other controller. The implementation for this method should delete the item from your model, and refresh the NSTableView.

The advantage to making an NSMenuItem out of the action is that:

  1. You can give the item a keyboard shortcut in Interface Builder. (Like the delete key.)
  2. Users who are not familiar with your application, afraid to press the delete key, or do not have access to a keyboard for whatever reason, can still make use of this functionality.
Cynewulf answered 12/1, 2011 at 14:3 Comment(4)
Hi, Craig. Your answer was not what I was really expecting, but it sounds pretty good! =D Thank you very much.Dickman
Haha yea, at first it seems like a pretty indirect approach, but it works extremely well, and takes care of a lot of headache.Cynewulf
@peetonn If you have two table views, you can just check which one has focus in your method implementation. And you can also add a focus listener if you want to dynamically update the menu item.Cynewulf
is this a "global" action or is it automatically applied directly to whichever Control has focus?Acute
H
12

I've implemented something similar to LTKeyPressTableView. However, I use array controllers, so in my subclass I added IBOutlet NSArrayController * relatedArrayController. Instead of handling delete request in a delegate, I handle it directly in the subclass since my subclass specifically deals with adding handling of Delete key. When I detect keypress for delete key, I'm just calling [relatedArrayController delete:nil];.

IRTableView.h:

#import <Cocoa/Cocoa.h>

@interface IRTableView : NSTableView {
    IBOutlet NSArrayController * relatedArrayController;
}

@end

and IRTableView.m:

#import "IRTableView.h"


@implementation IRTableView


- (void)keyDown:(NSEvent *)event
{
    // Based on LTKeyPressTableView.
    //https://github.com/jacobx/thoughtkit/blob/master/LTKeyPressTableView

    id delegate = [self delegate];

    // (removed unused LTKeyPressTableView code)

    unichar key = [[event charactersIgnoringModifiers] characterAtIndex:0];
    if(key == NSDeleteCharacter)
    {
        if([self selectedRow] == -1)
        {
            NSBeep();
        }

        BOOL isEditing = ([[self.window firstResponder] isKindOfClass:[NSText class]] && 
                          [[[self.window firstResponder] delegate] isKindOfClass:[IRTableView class]]);
        if (!isEditing) 
        {
            [relatedArrayController remove:nil];
            return;
        }

    }

    // still here?
    [super keyDown:event];
}

@end

End result is quite IB-friendly for me, and a quite simple solution for use in a Cocoa Bindings+Core Data application.

Hypogene answered 26/9, 2011 at 13:57 Comment(0)
A
12

There is no need to subclass and catch keyDown in NSViewController.

The Delete menu item in the menu Edit is connected to the selector delete: of First Responder. If there is no Delete menu item, create one and connect it to delete: of First Responder (red cube).

  • Assign a key equivalent to the Delete menu item ( or ⌘⌫)
  • In the view controller implement the IBAction method

    Swift: @IBAction func delete(_ sender: AnyObject)

    Objective-C: -(IBAction)delete:(id)sender

    and put in the logic to delete the table view row(s).

Antonia answered 29/6, 2017 at 18:17 Comment(12)
@iSavaDevRU Please write in English.Antonia
Please write in Russian :) Who needs it, he will understand. I feel uncomfortable reading English. But I read and translate, but is it difficult for you? :)Isobelisocheim
vadian, Ваш пример годится отчасти. Некая путаница с логикой будет, лишняя нагрузка. Подкласс будет отвечать только за свой объект. Лишних забот не нужно делать.Isobelisocheim
I translate into English. vadian, your example is partly suitable. There will be some confusion with logic, an extra load. A subclass will be responsible only for its object. No extra worries need to be done.Isobelisocheim
The community language of Stackoverflow is English, but there is also a Russian forumAntonia
I know. So, what is next? The questions here and there are different. Nevertheless, I registered here when there was no Russian forum. This does not change anything. People speak the language in which it is convenient.Isobelisocheim
It is not worth discussing this. Who needs it, he will understand. Do not, pass by.Isobelisocheim
I am here 7 years old, you are 3 years old. Take note.Isobelisocheim
But of course your answer has the right to be in simple applications or for AppleScriptObjC. I put a plus.Isobelisocheim
Thanks @vadian, this is an elegant method.Floatplane
is this a "global" action or is it automatically applied directly to whichever Control has focus?Acute
This is way to do it - an "input solution"; create alternate menu items - but make sure you do not mark as as such in IB - they are distinct!, for the <x] and [x> keys and do make them hidden and allowed when hidden. Much better than "output solution" - decoding keys.Coleridge
H
10

After 10.10, NSViewController is part of the responder chain. So the easiest way is to implement keyDown in your subclassed NSViewController

Harebell answered 8/12, 2014 at 1:39 Comment(1)
Good point about using the responder chain to avoid subclassing (where possible).Iago

© 2022 - 2024 — McMap. All rights reserved.