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
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:
- Delete
- (void)tableViewSelectionDidChange
- Create IBAction
- 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
}
}
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.
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.
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.
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
}