How to create an action from selecting a row from an NSTableView
Asked Answered
C

5

18

When I select a row from NSTableView, it is not running the code from tableViewSelectionDidChange method. I had put break points in this method and it is not even going into the method.

Any ideas? Am I missing something in my initialiser?

PersonController.h

#import <Foundation/Foundation.h>

@interface Person : NSObject {
  IBOutlet NSTableView *personsTable;
  NSMutableArray *personsList;
  NSMutableArray *personCollection;

  IBOutlet NSTextField *selectedPersonName;
  IBOutlet NSTextField *selectedPersonGender;
@private

}

- (void)tableViewSelectionDidChange:(NSNotification *)aNotification;

@end

PersonController.m

#import "PersonController.h"
#import "Person.h"


@implementation PersonController

- (id)init
{
    self = [super init];
    if (self) {
        personsList = [[NSMutableArray alloc] init];
        Person *person = [[Person alloc] init];

        // Create person 1
        person.name = @"Bob";
        person.gender = @"male";

        // Append to array
        [personsList addObject:person];
        [person release];

        // Create person 2
        person = [[Person alloc] init];
        person.name = @"Fred";
        person.gender = @"Unknown";

        // Append to array
        [personsList addObject:person];
        [person release];

        [personsTable reloadData];
    }

    return self;
}

- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
    return [personsList count];
}

- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
    Person *person = [personsList objectAtIndex:row];
    NSString *identifier = [tableColumn identifier];

    return [person valueForKey:identifier];
}

- (void)tableViewSelectionDidChange:(NSNotification *)aNotification
{
    NSInteger selectedRow = [personsTable selectedRow];
    if (selectedRow == -1)
    {
        // these should be localized, but use string constants here for clarity
        [selectedPersonName setStringValue:@"No selection"];
        [selectedPersonGender setStringValue:@"No selection"];
    }
    else
    {
        Person *selectedPerson = [personCollection objectAtIndex:selectedRow];

        [selectedPersonName setStringValue:[selectedPerson name]];
        [selectedPersonGender setStringValue:[selectedPerson gender]];
    }
}

- (void)dealloc
{
    [super dealloc];
}

@end
City answered 4/2, 2012 at 8:19 Comment(1)
I believe tableViewSelectionDidChange was not being called, because you had not set your NSTableView's delegate.Umiak
C
27

So the issue here was that I was trying to use the tableViewSelectionChange to trigger an event when a row in the NSTableColumn was clicked. As I couldn't get this working I took another approach which was to create an IBAction and link this to the NSTableView and I have found this to work well.

In order to do this, I did the following:

  1. Delete - (void)tableViewSelectionDidChange
  2. Create IBAction
  3. In the XIB file, create a Received Actions link between the IBAction and the NSTableView, in NSTableView connection inspector under "sent action" have "selector" action" connect this "selector" with that IBAction you want to trigger.

This is the IBAction in PersonController.m

- (IBAction)columnChangeSelected:(id)sender
{
    NSInteger selectedRow = [personsTable selectedRow];

    if (selectedRow != -1) {
        NSLog(@"Do something with selectedRow!");
    }
    else {
        // No row was selected
    }
}
City answered 7/2, 2012 at 22:0 Comment(0)
B
5

To expand on Coderama's answer, the equivalent in code is:

tableView.target = self;
tableView.action = @selector(tableViewClicked:);

then implement the method in the same class:

- (void)tableViewClicked:(id)sender {
    // This will return -1 if the click did not land on a row
    NSLog(@"tableView.clickedRow = %ld", tableView.clickedRow);

    // This will return -1 if there is no row selected.
    NSLog(@"tableView.selectedRow = %ld", tableView.selectedRow);
}

The table view will call its action method on the target you set every time the view is clicked. This will allow you to know every time a row is selected, even if it is already selected.

Brocky answered 14/1, 2017 at 4:41 Comment(0)
U
3

Solution in Swift 3 for most of the events:

Inside tableview view controller :

override func viewDidLoad() {
    super.viewDidLoad()

    tableView.target = self
    tableView.action = #selector(tableViewDidClick)
}

func tableViewDidClick(){
    let row = tableView.clickedRow
    let column = tableView.clickedColumn
    let unselected = -1

    if row == unselected && column == unselected{
        tableViewDidDeselectRow()
        return
    }else if row != unselected && column != unselected{
        tableViewDidSelectRow(row)
        return
    }else if column != unselected && row == unselected{
        tableviewDidSelectHeader(column)
    }
}

private func tableViewDidDeselectRow() {
    // clicked outside row
}

private func tableViewDidSelectRow(_ row : Int){
    // row did select
}

private func tableviewDidSelectHeader(_ column : Int){
    // header did select
}
Unlade answered 8/7, 2017 at 14:5 Comment(0)
H
2

Make sure that the instance of PersonController is connected to personsTable as the delegate. I don't know how you created the NSTableView, but if your are using Nibs, you can set the delegate in Interface Builder. If not, you can add [personsTable setDelgate:self] in the init method and [personsTable setDelegate:nil] in the dealloc method.

Also, you are leaking personsList. Add [personsList release] to your delegate method.

Hackneyed answered 4/2, 2012 at 11:58 Comment(1)
The instance of PersonController is already connected to personsTable as the delegate. Thanks for spotting the personsList leak though.City
C
2

For others who bump into this issue: other reason for this delegate not being called could be because the row is already selected. So it wont notify unless a new selection is made. If you still want to be able to receive notifications on every click deselect all rows once you get notified.

Now this will make 2 calls to your delegate, one for getting selected and other for getting deselected. Second call is not needed so add a condition on top to check the [tableView selectedRow] this would return -1 when no rows are selected.

Cown answered 10/12, 2013 at 3:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.