Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

rafer11

macrumors newbie
Original poster
Mar 21, 2011
11
0
I am doing a challenge from Aaron Hillegass's book.

The application is supposed to have an NSTableView(with 2 columns) and two Buttons. The two buttons will be 'Add' and 'Delete'. When you press these buttons they will add an 'Employee' to the NSTableView, and Delete will remove a selected Employee.

When I click on my buttons nothing happens. In the example you are not supposed to use an NSArrayController or the bindings mechanism.

I suspect that I have missed doing something in Interface Builder. He mentions "In MyDocument.xib, you will set the identifier of each column to be the name of the variable you would like displayed."

Here is my code:

Person.h
Code:
#import <Foundation/Foundation.h>


@interface Person : NSObject {
	NSString *personName;
	float expectedRaise;
}

@property (readwrite, copy) NSString *personName;
@property (readwrite) float expectedRaise;

@end

Person.m
Code:
@implementation Person

@synthesize personName;
@synthesize expectedRaise;

-(id)init
{
	[super init];
	expectedRaise = 5.0;
	personName = @"New Person";
	return self;
}

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

-(void)setNilValueForKey:(NSString *)key
{
	if ([key isEqual:@"expectedRaise"])
	{
		[self setExpectedRaise:0.0];
	}
	else
	{
		[super setNilValueForKey:key];
	}
}

@end

MyDocument.h
Code:
#import <Cocoa/Cocoa.h>
@class Person;

@interface MyDocument : NSDocument
{
	NSMutableArray *employees;
	IBOutlet NSTableView *tableView;
}

-(IBAction)createEmployee:(id)sender;
-(IBAction)removeEmployee:(id)sender;

@end

MyDocument.m
Code:
#import "MyDocument.h"

@implementation MyDocument

- (id)init
{
    self = [super init];
    if (self) 
	{
		employees = [[NSMutableArray alloc] init];
    }
    return self;
}

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

-(IBAction)createEmployee:(id)sender
{
	Person *newEmployee = [[Person alloc] init];
	
	[employees addObject:newEmployee];
	[newEmployee release];
	[tableView reloadData];	
}

-(IBAction)removeEmployee:(id)sender
{
	NSIndexSet *rows = [tableView selectedRowIndexes];
	
	if ([rows count] == 0)
	{
		NSBeep();
		return;
	}
	
	[employees removeObjectsAtIndexes:rows];
	[tableView reloadData];
}

-(int)numberOfRowsInTableView:(NSTableView *)aTableView
{
	return [employees count];
}

-(id)tableView:(NSTableView *)aTableView
	objectValueForTableColumn:(NSTableColumn *)aTableColumn
		   row:(int)rowIndex
{
	NSString *identifier = [aTableColumn identifier];
	
	Person *person = [employees objectAtIndex:rowIndex];
	
	return [person valueForKey:identifier];
}

-(void)tableView:(NSTableView *)aTableView
  setObjectValue:(id)anObject
  forTableColumn:(NSTableColumn *)aTableColumn
			 row:(int)rowIndex
{
	NSString *identifier = [aTableColumn identifier];
	Person *person = [employees objectAtIndex:rowIndex];
	[person setValue:anObject forKey:identifier];
}

- (NSString *)windowNibName
{
    //System Generated code here that I haven't touched but removed from example for readability
}

- (void)windowControllerDidLoadNib:(NSWindowController *) aController
{
    //System Generated code here that I haven't touched but removed from example for readability
}

- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
{
    //System Generated code here that I haven't touched but removed from example for readability
}

- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
{
    //System Generated code here that I haven't touched but removed from example for readability
}

@end

While highlighting the first Table Column, in the Attributes tab I have Identifier set to variable personName. In the second Table column attributes tab I have Identifier set to expectedRaise.

Do I need to set the Class Identity of the NSTableView to 'Person'? It won't let me so I'm guessing no.

Do I need to Control-drag from the MyDocument object to the NSTableView? I did that and no luck.

What am I missing to make this needed connection?

Thank you.
 

jiminaus

macrumors 65816
Dec 16, 2010
1,449
1
Sydney
Insert

Code:
NSLog(@"%s entered", __PRETTY_FUNCTION__);

at the top of your methods.

Recompile and then push the add button.

Do you see a log message in the XCode console window about createEmployee: having been entered? If you don't there's a problem with the connection between your button and your document.

If you do, do see a log message about numberOfRowsInTableView: being been entered. If not, is your document set up to be the table view's data source?
 

rafer11

macrumors newbie
Original poster
Mar 21, 2011
11
0
Do you see a log message in the XCode console window about createEmployee: having been entered? If you don't there's a problem with the connection between your button and your document.

If you do, do see a log message about numberOfRowsInTableView: being been entered. If not, is your document set up to be the table view's data source?

For the Add button I do see a createEmployee being entered, and person init being entered. For the delete button removeEmployee is not entered.

How do I set up my document to be my table view's data source?

[Also, in my IDE I get a warning of "Receiver 'Person' is a forward class and corresponding @interface may not exist". What does that mean?]
 

Sydde

macrumors 68030
Aug 17, 2009
2,552
7,050
IOKWARDI
How do I set up my document to be my table view's data source?
if ctrl-dragging does not work, think about what is needed. Ctrl-drag informs the source (where you click) that the destination exists. Is it necessary for the table to be aware of the document or for the document to be aware of the table?
[Also, in my IDE I get a warning of "Receiver 'Person' is a forward class and corresponding @interface may not exist". What does that mean?]
Try #import instead, I think it makes more sense in this case. It would appear that @class will always generate a warning. It informs the compiler that the class exists so that you do not get undefined type errors. "Forward" means that in the stream of consciousness of the compiler, so to speak, something is happening ahead, after this point in compilation.
 

jiminaus

macrumors 65816
Dec 16, 2010
1,449
1
Sydney
How do I set up my document to be my table view's data source?

Go back through the chapter you've just been through. I'm sure Hillgrass will have told how to do this.

Hint: if you can't to it in Interface Builder, you can do via code in awakeFromNib.

[Also, in my IDE I get a warning of "Receiver 'Person' is a forward class and corresponding @interface may not exist". What does that mean?]

Yes, you need to #import "Person.h" at the top of Person.m analogously to what you (or XCode) did at the top to MyDocument.m.

And as Sydde says, replace @class Person with #import "Person.h"
 
Last edited:

rafer11

macrumors newbie
Original poster
Mar 21, 2011
11
0
if ctrl-dragging does not work, think about what is needed. Ctrl-drag informs the source (where you click) that the destination exists. Is it necessary for the table to be aware of the document or for the document to be aware of the table?

My control-drag was from the MyDocument NSObject in IB to the NSTableView. I highlighted each separate NSTableColumn, and linked one to personName and the other to expectedRaise.

However, now that I think about it, that doesn't fully make sense to me.

NSTableView doesn't know about my NSMutableArray employees. So I'm creating a new person and adding it to the array, then doing a reloadData on my NSTableView, but I don't believe I've ever linked the array and tableview.

Although, nevermind... that's get done with the
-(id)tableView:(NSTableView *)aTableView
objectValueForTableColumn:(NSTableColumn *)aTableColumn
row:(int)rowIndex
method I believe so that should be fine.

It returns a person object.

As far as I can tell, it seems like I'm fine.

I've set the identifier: in IB to my 2 NSTableColumns to 'personName' and 'expectedRaise'. I've written both the objectValueForTableColumn method and setObjectValue method. They get the identifier, and the person, and then do either a valueForKey or setValue:forKey: with that identifier.

Only thing I can think of is that my identifier's should be named employees.personName as opposed to just personName. I'll go give that a try and report back.
 

rafer11

macrumors newbie
Original poster
Mar 21, 2011
11
0
Got it! I had MyDocument referencing my tableView, but I didn't have the dataSource set as well. I didn't realize I had to set both :S

*Sigh* the pains of learning a new language.

Thanks guys.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.