doctor_ew
doctor_ew

Reputation: 123

How to add an NSArray of filenames to a NSTableView? - cocoa

I need help figuring out how to display the contents of an NSArray into a NSTableView. My NSArray is filled with (or at least I think it is) filenames from a directory. I use NSFileManager to get the names of files in a directory and then I load that info into a NSArray. But I can't figure out how to load the NSArray into the NSTableView.

AppDelegate.h

#import <Cocoa/Cocoa.h>

@interface AppDelegate : NSObject <NSApplicationDelegate> {
IBOutlet NSTableView *tableView;

NSArray *list;
IBOutlet NSTextField *text;

NSFileManager *manager;
NSString *path;
NSString *pathFinal;
}

@property (assign) IBOutlet NSWindow *window;

- (IBAction)listArray:(id)sender;

@end

AppDelegate.m

#import "AppDelegate.h"

@implementation AppDelegate

@synthesize window = _window;

- (int)numberOfRowsInTableView:(NSTableView *)tableView
{
    return [list count];
}

- (id)tableView:(NSTableView *)tableView
objectValueForTableColumn:(NSTableColumn *)tableColumn
        row:(int)row
{
    return [list objectAtIndex:row];
}

- (IBAction)listArray:(id)sender {
    path = @"~/Library/Application Support/minecraft/bin/";
    pathFinal = [path stringByExpandingTildeInPath];
    list = [manager directoryContentsAtPath:pathFinal];

    [tableView reloadData];
}

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

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{

}

@end

Upvotes: 0

Views: 1658

Answers (1)

Rob Keniger
Rob Keniger

Reputation: 46020

There are two ways to do this: Cocoa Bindings using NSArrayController or by implementing the NSTableDataSource protocol in an object and assigning that object as the table view's datasource.

It looks like you have already half-implemented the NSTableViewDataSource method. You need to add the protocol declaration to your interface to indicate that your AppDelegate object implements the protocol:

@interface AppDelegate : NSObject <NSApplicationDelegate, NSTableViewDataSource> 

You have already implemented the required datasource methods, so in theory everything should be working, but you possibly have not set your AppDelegate object as the table view's datasource. You can do this in code:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    [tableView setDatasource:self];
}

Alternatively, you can assign the datasource in Interface Builder by setting the table view's datasource outlet to your AppDelegate instance.

However, the main problem you have is that you are assigning an autoreleased object to your list ivar, and it is being released before the table view reloads.

Your listArray method is problematic. There is no reason for path and pathFinal to be instance variables. They are only used once, so should be locally scoped. In fact, since path is a constant, it should be declared separately:

//this should go in the top of your .m file, after the #import directives
static NSString* minecraftPath = @"~/Library/Application Support/minecraft/bin/";

- (IBAction)listArray:(id)sender 
{
    NSString* path = [minecraftPath stringByExpandingTildeInPath];

    //you want to hang onto the array that is returned here, so you must retain it
    //however, if you don't release the existing value, it will be leaked
    [list release];
    list = nil;
    list = [[manager directoryContentsAtPath:pathFinal] retain];
    [tableView reloadData];
}

- (void)dealloc
{
    //because you retained it, you must release it
    [list release];
    [super dealloc];
}

A much better way to do this would be to declare list as a property and synthesize its accessors:

.h:

@interface AppDelegate : NSObject <NSApplicationDelegate, NSTableViewDataSource> {
 ...
}
...
@property (retain) NSArray* list;
...

.m:

@implementation AppDelegate
@synthesize list;
...

You can then use the property and it handles the retain/release for you:

- (IBAction)listArray:(id)sender 
{
    NSString* path = [minecraftPath stringByExpandingTildeInPath];

    //you've set the property to use retain, so the synthesized accessor does that for you
    self.list = [manager directoryContentsAtPath:pathFinal];
    [tableView reloadData];
}

- (void)dealloc
{
    //you still need to release when done
    self.list = nil;
    [super dealloc];
}

Upvotes: 1

Related Questions