Question

I've successfully created an NSCollectionView and added a label to the view prototype in IB, bound to a property of my represented object. I now want to programatically create an NSButton and NSTextField with the NSTextField bound to a property of my represented object. When the button is clicked I want to show and hide the NSTextField.

The problem I've come across is if I put my initialization code for my controls in the view's initWithCoder method, and the binding in the view's awakeFromNib, the binding doesn't get hooked up. If I put the initialization for my controls in the awakeFromNib, when the button is clicked, I don't have access to the controls in my view (they are null when printed out using NSLog).

From what I can tell it looks like the issue may be that the way NSCollectionView works is, it creates an instance of the view, then copies it for how every many objects are in the collection view. How do I get the the buttons to initialize and the binding to work with the copy of the prototype?

Below is my initialization code and my binding in the awakeFromNib for my subclassed view:

SubView.h

@interface SubView : NSView {
    NSButton *button;
    NSTextField *textField;
    IBOutlet NSCollectionViewItem *item; // Connected in IB to my NSCollectionViewItem
}

- (IBAction)buttonClicked:(id)sender;

@end

SubView.m

@implementation SubView

- (id)initWithCoder:(NSCoder *)decoder
{
    id view = [super initWithCoder:decoder];

    button = [[NSButton alloc] initWithFrame:NSMakeRect(50, 95, 100, 20)];
    [button setTitle:@"Begin Editing"];
    [button setTarget:self];
    [button setAction:@selector(buttonClicked:)];
    [self addSubview:button];

    textField = [[NSTextField alloc] initWithFrame:NSMakeRect(10, 10, 100, 75)];
    [self addSubview:textField];

    return(view);
}

- (void)awakeFromNib
{   
        // Bind the textField to the representedObject's name property
        [textField bind:@"value" 
       toObject:item 
        withKeyPath:@"representedObject.name" 
        options:nil];
}

- (IBAction)buttonClicked:(id)sender
{
    [button setTitle:@"End Editing"];
    [textField setHidden:YES];
}

@end
Was it helpful?

Solution

This sounds similar to something I just did, so maybe it's what you need.

Subclass NSCollectionView and override:

- (NSCollectionViewItem *)newItemForRepresentedObject:(id)object

In newItemForRepresentedObject:, retreive the view item, then add your controls and any programmatic bindings:

@implementation NSCollectionViewSubclass

- (NSCollectionViewItem *)newItemForRepresentedObject:(id)object {

    // Allow the superclass to create or copy the collection view item
    NSSCollectionViewItem *newItem = [super newItemForRepresentedObject:object];

    // Get the new item's view so you can mess with it
    NSView *itemView = [newItem view];

    //
    // add your controls to the view here, bind, etc
    //

    return newItem;
}

@end

Hopefully this is somewhere close to where you need to be...

OTHER TIPS

-awakeFromNib is not called on the view for a NSCollectionViewItem if that view is in the same nib as the NSCollectionView, but it is called if you put the view in a separate nib.

  • Create an empty nib file (BlahBlahCollectionViewItem.nib).
  • Cut the collection item view out of whatever nib you have it in
  • Paste it into the new nib file
  • Change the class of its owner to NSCollectionViewItem.
  • Connect the view outlet on the owner to the newly pasted view
  • Open the nib file containing the NSViewController
  • Select the associated NSViewControllerItem
  • Change its Nib Name property to the name of the new nib
  • Keep your code in -awakeFromNib

-awakeFromNib is not called for views copied from the prototype NSCollectionViewItem. Put your binding code in initWithCoder: and you should be fine.

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