Question

I've been stumped by this for the whole day, PLEASE HELP. I've searched high and low and can't figure this out.

I've created an OutlineViewController which I want to handle my outline view tasks and datasource. I was able to have it work just fine if I used appDelegate as my datasource and connected it to the OutlineView in the interface builder. But I can't seem to get it to work when setting the datasource programatically to its self (or programmatically to appdelegate for that matter). Is this being overrided somehow?

Strange thing I noticed. When debugging, with the appdelegate connected as datasource via interface builder the mainOutlineView property has its datasource listed as nothing. When I set it programmatically it shows its linked up, but doesn't work.

Any suggestions or examples please!

OutlineViewController.h

#import <Cocoa/Cocoa.h>

@interface OutlineViewController : NSOutlineView <NSOutlineViewDataSource, NSOutlineViewDelegate>
{
    NSMutableDictionary *firstParent;
    NSMutableDictionary *secondParent;
    NSArray *list;
}

@end

OutlineViewController.m

#import "OutlineViewController.h"

static NSUserDefaults *ud1 = nil;

@implementation OutlineViewController

#pragma mark Initialization

- (id)init
{
    ud1 = [NSUserDefaults standardUserDefaults];
    if (self = [super init]) {
        // use ud if you want to initialize via init
        // ud = [NSUserDefaults standardUserDefaults];
        firstParent = [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"James",@"parent",[NSArray arrayWithObjects:@"Mary",@"Charlie", nil],@"children", nil];

        secondParent = [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"Elisabeth",@"parent",[NSArray arrayWithObjects:@"Jimmie",@"Kate", nil],@"children", nil];

        list = [NSArray arrayWithObjects:firstParent,secondParent, nil];
        [self setDataSource:self];
        [self setDelegate:self];

        NSLog(@"init OutlineViewController");
    }
    return self;
}

#pragma mark NSOutlineView Data Source Methods

- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{
    if ([item isKindOfClass:[NSDictionary class]] || [item isKindOfClass:[NSArray class]]) {
        return YES;
    }else {
        return NO;
    }
}

- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
{    
    if (item == nil) { //item is nil when the outline view wants to inquire for root level items
        return [list count];
    }

    if ([item isKindOfClass:[NSDictionary class]]) {
        return [[item objectForKey:@"children"] count];
    }

    return 0;
}

- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
{

    if (item == nil) { //item is nil when the outline view wants to inquire for root level items
        return [list objectAtIndex:index];
    }

    if ([item isKindOfClass:[NSDictionary class]]) {
        return [[item objectForKey:@"children"] objectAtIndex:index];
    }

    return nil;
}

- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)theColumn byItem:(id)item
{

    if ([[theColumn identifier] isEqualToString:@"children"]) {
        if ([item isKindOfClass:[NSDictionary class]]) {
            return [NSString stringWithFormat:@"%li kids",[[item objectForKey:@"children"] count]];

        }
        return item;
    } else {
        if ([item isKindOfClass:[NSDictionary class]]) {
            return [item objectForKey:@"parent"];
        }
    }

    return nil;
}

- (BOOL)outlineView:(NSOutlineView *)outlineView shouldExpandItem:(id)item
{    
    return YES;
}

- (BOOL)outlineView:(NSOutlineView *)outlineView shouldCollapseItem:(id)item
{
    return YES;
}

- (void)dealloc
{
    NSLog(@"deallocating %@", self);
}

@end

AppDelegate.h

#import <Cocoa/Cocoa.h>
#import "OutlineViewController.h"

@interface AppDelegate : NSObject <NSApplicationDelegate>
{
    id<NSOutlineViewDataSource> oldDataSource;
    id<NSOutlineViewDelegate> oldDelegate;
}

@property (assign) IBOutlet NSWindow *window;
@property (weak) IBOutlet NSTextField *singerName;
@property (nonatomic) IBOutlet OutlineViewController *mainOutlineView;

- (IBAction)addSinger:(id)sender;

@end

AppDelegate.m

#import "AppDelegate.h"

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    NSLog(@"appDidFinishLaunching");
    //_mainOutlineView = self.mainOutlineView.init;
}

- (void)awakeFromNib
{
    NSLog(@"awakeFromNib");
    self.mainOutlineView = [[OutlineViewController alloc] init];    

    [_mainOutlineView reloadData];
}

- (IBAction)addSinger:(id)sender
{
    NSLog(@"Add Singer Clicked %@", self.singerName.stringValue);

    //NSMutableArray *tempArray = [[NSMutableArray alloc] init];
    //tempArray = [[firstParent valueForKey:@"children"] mutableCopy];

    //[tempArray addObject:self.singerName.stringValue];
    //[firstParent setObject:tempArray forKey:@"children"];

    self.singerName.stringValue = @"";
    [self.mainOutlineView reloadData];
}
@end
Was it helpful?

Solution

You shouldn't be subclassing the NSOutlineView to make an OutlineViewController class.

Your delegate/datasource object could inherit from lots of things. But it shouldn't inherit from NSView. With MVC, you have situations where the MC parts get lumped together, but if you're making a C, it most definitely doesn't subclass a V.

If you want to instantiate the controller object in IB, you can just have your class inherit from NSObject, and drag an object cube into the Nib, and make its custom class your custom class. But you don't necessarily need the controller object in IB.

In OSX, the delegate and datasource protocols are often implemented by whatever the File's Owner of the Nib is. So if it's a window nib, that would be a NSWindowController. If it's a view nib, it would be a NSViewController. You could subclass either of those, and set the file's owner to your custom controller class, and you implement the protocols in that class, you can then drag the outlineview datasource/delegate connections in IB up to file's owner, and the Nib loading process would ensure the outlineview was connected to your controller methods implementations.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top