Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSTableView to allow user to choose which columns to display

Anyone knows how to allow users to choose which columns of an NSTableView to be displayed by right clicking and then selecting? Just like iTunes does.

like image 460
David James Avatar asked Nov 25 '12 18:11

David James


2 Answers

I have implemented this and the following should be usable without any subclassing.

First implement an empty menu in IB and connect to the menu output of the Table Header View.

This method (called from awakeFromNib) constructs the contents of the menu from the header (and includes a test to prevent users hiding a primary column)

- (void)initViewHeaderMenu:(id)view {
    //create our contextual menu
    NSMenu *menu = [[view headerView] menu];
    //loop through columns, creating a menu item for each
    for (NSTableColumn *col in [view tableColumns]) {
        if ([[col identifier] isEqualToString:COLUMNID_NAME])
            continue;   // Cannot hide name column
        NSMenuItem *mi = [[NSMenuItem alloc] initWithTitle:[col.headerCell stringValue]
                                                    action:@selector(toggleColumn:)  keyEquivalent:@""];
        mi.target = self;
        mi.representedObject = col;
        [menu addItem:mi];
    }
    return;
}

This invokes the following to do the actual hiding/unhiding

- (void)toggleColumn:(id)sender {
    NSTableColumn *col = [sender representedObject];
    [col setHidden:![col isHidden]];
}

You also need to set the delegate of the Menu and implement the following to set states:-

#pragma mark NSMenu Delegate Methods
-(void)menuWillOpen:(NSMenu *)menu {
    for (NSMenuItem *mi in menu.itemArray) {
        NSTableColumn *col = [mi representedObject];
        [mi setState:col.isHidden ? NSOffState : NSOnState];
    }
}
like image 147
Milliways Avatar answered Oct 09 '22 02:10

Milliways


I extended Milliways' great answer based on this blog post and added the following functionality:

  • Display checkmarks for visible columns
  • Persist the settings using NSUserDefaults

Initial Setup:

// Your intial Startup code
[self setupHeaderMenu:self.yourTableView];

Creating the menu:

Important: Because of col.identifier you'll have to set an "Identify Identifier" for each Table view column in IB for this to work.

#pragma mark - Show Hide Columns
- (void)setupHeaderMenu:(NSTableView *)tableView {

    NSDictionary *savedCols = [[NSUserDefaults standardUserDefaults] dictionaryForKey:kUserDefaultsKeyVisisbleColumns];

    NSMenu *menu = [NSMenu new];
    for (NSTableColumn *col in tableView.tableColumns) {

        NSMenuItem *mi = [[NSMenuItem alloc] initWithTitle:[col.headerCell stringValue]
                                                    action:@selector(toggleColumn:)
                                             keyEquivalent:@""];
        mi.target = self;

        if(savedCols){
            BOOL isVisible = [savedCols[col.identifier] boolValue];
            [col setHidden:!isVisible];
        }

        mi.state = (col.isHidden ? NSOffState: NSOnState);
        mi.representedObject = col;
        [menu addItem:mi];
    }

    tableView.headerView.menu = menu;
    return;
}

The toggle method

The toggle method saves the new configuration in NSUserDefaults

- (void)toggleColumn:(NSMenuItem *)menu {
    NSTableColumn *col = menu.representedObject;

    BOOL shouldHide = !col.isHidden;
    [col setHidden:shouldHide];

    menu.state = (col.isHidden ? NSOffState: NSOnState);

    NSMutableDictionary *cols = @{}.mutableCopy;
    for( NSTableColumn *column in self.yourTableView.tableColumns){
        cols[column.identifier] = @(!column.isHidden);
    }

    [[NSUserDefaults standardUserDefaults] setObject:cols forKey:kUserDefaultsKeyVisibleColumns];
    if(shouldHide){
        [self.yourTableView sizeLastColumnToFit];
    } else {
        [self.yourTableView sizeToFit];
    }
}

Menu delegate

-(void)menuWillOpen:(NSMenu *)menu {
    for (NSMenuItem *mi in menu.itemArray) {
        NSTableColumn *col = [mi representedObject];
        [mi setState:col.isHidden ? NSOffState : NSOnState];
    }
}

The Result

So now you can check / uncheck each column and the configuration will be saved even after a restart of your App.

hide columns

like image 44
Besi Avatar answered Oct 09 '22 03:10

Besi