NSTableView

Sage
Posts: 1,066
Joined: 2004.07
Post: #1
It's been a while (I have no internet at the current time) so I have another question. Sorry if it's easily searchable but I really don't have time (work in a couple minutes).

Basically, can anyone quickly explain how I can use an NSMutableArray of NSMutableDictionary's in an NSTableView using Cocoa and IB? Any sites online? Tried Apple's Docs for a few days but it's not really helping.
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #2
Are you talking about using bindings? Or manual?

I've never done and bindings programming, but you might want to check out the tutorials on cocoadevcentral.com.

Regarding manual control, it'd be easy enough. Just provide a data source with the two methods:

Code:
- (int)numberOfRowsInTableView:(NSTableView *)aTableView;
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex;

The first would return the number of elements in your array, and the second would query the NSDictionary at index 'rowIndex' in the array for the object with the key [aTableColumn identifier] -- provided of course that you set up your table with column identifiers that correspond to the keys in your dict.
Quote this message in a reply
Moderator
Posts: 133
Joined: 2008.05
Post: #3
Okay, here is my step by step tutorial, because it is so much better than Carbon that it excites me.

Interface builder:
1. Place your NSTableView wherever you want in your layout.
2. Add a descriptive title of your NSTableView as an outlet to your controller class. By descriptive, I mean something that will seperate it from any other table view you might add.
3. Connect the controller outlet to the NSTableView, and connect the NSTableView to the controller's delegate and dataSource outlets.
4. Set up your table view for whatever data you may need, creating columns etc., and properly identifying them(a descriptive identifier string in their attributes box).
5. Make the files for your controller, or add IBOutlet NSTableView* whateverYouNamedIt; to your controller class. You're doen with the interface builder part.

XCode steps.
These go in your .h file for your controller.
1. Add a NSMutablyArray to your controller class. Give it a similar name to your tableview. For example, if your tableView is called beverageTableView, call the array beverageDataStorage, or beverageDataLookup. It makes it easier to remember, and gives you an idea of it's functionality.
2. Add another function to your class called - (void)createBeverageTableRecord;

These are in your .m file
3. Implement your awakeFromNib function, including these lines of code.
Code:
if(beverageDataStorage == nil) //allocate when we finally add an entry.
             beverageDataStorage = [[NSMutableArray alloc] init];
4. Let's define your createBeverageTableRecord function first. This function adds data to be shown by your tableView, you call this function explicitly to add new data.
Code:
- (void)createBeverageTableRecord:(NSString*)bevName cansLeft:(int)n
{
      
        //We're going to configure this dictionary entry to be one row of the
       //table view
       NSMutablyDictionary* beverageEntry = [[NSMutableDictionary alloc] init];

       //remember when we made our identifiers in interface builder?
       //The tableView will look at it's NSArray at NSDictionary's inside it, and match
       //the columns to the key's... so we stay consistent with our naming.      
       [beverageEntry setObject:bevName forKey:@"beverageName"];
       //This one shows you how to put an int in there
       [beverageEntry setObject:[NSNumber numberWithInt:cansLeft] forKey:@"beverageRemaining"];
        
       //Now we put the record into our storage data, and release this function's copy
       [beverageDataStorage addObject:beverageEntry];
       [beverageEntry release]; //the array is retains a copy
  
       //Now tell the table to re-read the data
       [ourTableFromInterfaceBuilder reloadData];
}

5. Now we need to add functionality to the table so that it knows how to read the data. You need to add two functions to your controller(the one we set up as a dataSource/delegate) for the reading of data. These are the minimum functions to support a NSTableView. You might wonder why there is a check to see if it's our tableView - it's because if a controller class has more than one table view, it will be getting data from different places. Doing this ensures that you don't have to create a new controller class each time you put a table in your GUI. By the way, you don't call these functions explicitly.
Code:
- (int)numberOfRowsInTableView:(NSTableView*)aTableView
{
    if(aTableView == ourTableFromInterfaceBuilder)
        return [beverageDataStorage count];

    return 0; //something went wrong!
}
Code:
- (id)tableView:(NSTableView*)aTableView objectValueForTableColumn:(NSTableColumn*)aTableColumn row:(int)rowIndex
{
    if(aTableView == ourTableFromInterfaceBuilder)
    {
        id record, value;
              //Get the dictionary at the correct row.
        record = [beverageDataStorage objectAtIndex:rowIndex];
              //Get the identifier to find the key for the dictionary entry we
              //are trying to return.
        value = [record objectForKey:[aTableColumn identifier]];
        return value;
    }
    return nil;
}

6. Now that you're dataSource is implemented in such a way where you can view all the data in a tableView, there are times when you want to change data inside the tableView. You need to add this function to you're dataSource.
Code:
- (void)tableView:(NSTableView*)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn*)aTableColumn row:(int)rowIndex
{
    if(aTableView == ourTableFromInterfaceBuilder)
    {
        NSString* ident = (NSString*)[aTableColumn identifier];
        if([ident isEqual:@"beverageName"])
        {
            NSString* s = (NSString*)anObject;
            NSMutableDictionary* dictEntry = [beverageDataStorage objectAtIndex:rowIndex];
            [dictEntry setObject:s forKey:@"beverageName"];
        }
        else if([ident isEqual:@"beverageRemaining"])
        {
            NSNumber* n = (NSNumber*)anObject;
            NSMutableDictionary* dictEntry = [beverageDataStorage objectAtIndex:rowIndex];
            [dictEntry setObject:n forKey:@"beverageRemaining"];
        }
              //Tell the table we need new data loaded
        [ourTableFromInterfaceBuilder reloadData];
    }
}

And that should about do it. I didn't really make sure this works, but it's a copy and paste of my working code, let me know if you have additional questions.
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Fake NSTableView Coin 1 2,187 Dec 5, 2005 08:48 AM
Last Post: TomorrowPlusX
  custom NSTableView honkFactory 8 5,027 May 17, 2005 06:56 PM
Last Post: PowerMacX