Setting the identifier of each column in an NSTableView.

Discussion in 'Mac Programming' started by rafer11, Mar 22, 2011.

  1. rafer11 macrumors newbie

    Joined:
    Mar 21, 2011
    #1
    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.
     
  2. jiminaus macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Location:
    Sydney
    #2
    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?
     
  3. rafer11 thread starter macrumors newbie

    Joined:
    Mar 21, 2011
    #3
    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?]
     
  4. Sydde macrumors 68020

    Sydde

    Joined:
    Aug 17, 2009
    #4
    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?
    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.
     
  5. jiminaus, Mar 23, 2011
    Last edited: Mar 23, 2011

    jiminaus macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Location:
    Sydney
    #5
    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.

    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"
     
  6. rafer11 thread starter macrumors newbie

    Joined:
    Mar 21, 2011
    #6
    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.
     
  7. rafer11 thread starter macrumors newbie

    Joined:
    Mar 21, 2011
  8. rafer11 thread starter macrumors newbie

    Joined:
    Mar 21, 2011
    #8
    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.
     

Share This Page