How to use NSCollectionViewItem without subclassing it?

3.1k views Asked by At

I am writing a very simple macOS application that I'd like to show a few images in a collection view. I don't need any special behavior for how they are displayed. The docs for NSCollectionViewItem say:

The default implementation of this class supports the creation of a simple item that displays a single image or string.

That is what I want. However, I can't find any information on how to create a default NSCollectionViewItem.

The documentation for NSCollectionView states:

Every data source object is required to implement the following methods:

collectionView:numberOfItemsInSection:

collectionView:itemForRepresentedObjectAtIndexPath:

The second method above returns an NSCollectionViewItem. From reading examples I gather that the traditional way of creating an NSCollectionViewItem in this case is to call:

NSCollectionViewItem*   newCollectionViewItem   = [imageCollectionView makeItemWithIdentifier:<some identifier>
                                                                                     forIndexPath:indexPath];

The problem is that I don't understand what <some identifier> should be. I don't have a nib that contains an NSCollectionViewItem because I'm not subclassing it or customizing it in any way. I've tried adding the following to my data source:

- (void)awakeFromNib
{
    [imageCollectionView registerClass:[NSCollectionViewItem class]
                 forItemWithIdentifier:@"Image"];
}

where imageCollectionView is the NSCollectionView in question. And then in my - (NSCollectionViewItem *)collectionView:itemForRepresentedObjectAtIndexPath: method, I call:

NSCollectionViewItem*   newCollectionViewItem   = [imageCollectionView makeItemWithIdentifier:@"Image"
                                                                                     forIndexPath:indexPath];

But this throws an exception and prints this to the console:

2016-12-19 17:51:27.463 MyApp[28177:3926764] -[NSNib _initWithNibNamed:bundle:options:] could not load the nibName: NSCollectionViewItem in bundle (null).

followed by a stack trace.

So how do I go about creating and using an NSCollectionViewItem that isn't subclassed or modified in any way?

2

There are 2 answers

6
clemens On BEST ANSWER

Here is a very simple example which uses a Nib for the item's prototype:

@interface ViewController()

@property (weak) IBOutlet NSCollectionView *collectionView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    NSNib *theNib = [[NSNib alloc] initWithNibNamed:@"Item" bundle:nil];

    [self.collectionView registerNib:theNib forItemWithIdentifier:@"item"];
}

#pragma mark NSCollectionViewDataSource

- (NSInteger)numberOfSectionsInCollectionView:(NSCollectionView *)inCollectionView {
    return 1;
}

- (NSInteger)collectionView:(NSCollectionView *)inCollectionView numberOfItemsInSection:(NSInteger)inSection {
    return 10;
}

- (NSCollectionViewItem *)collectionView:(NSCollectionView *)inCollectionView itemForRepresentedObjectAtIndexPath:(NSIndexPath *)inIndexPath {
    NSCollectionViewItem *theItem = [inCollectionView makeItemWithIdentifier:@"item" forIndexPath:inIndexPath];
    NSTextField *theLabel = (NSTextField *)theItem.view;

    theLabel.stringValue = [NSString stringWithFormat:@"%d.%d", (int)inIndexPath.section, (int)inIndexPath.item];
    return theItem;
}

@end

The NIB contains just a NSCollectionViewItem with a text field as view.

Addendum: I think you should create a NSCollectionViewItem.xib for the registerClass variant. A view controller will search for a NIB with its class name, if you doesn't create its view manually in loadView. Thus, you can't use plain NSCollectionViewItem without a NIB for registering a class, because of makeItemWithIdentifier:forIndexPath: will access the view of the item.

0
Jonas Due Vesterheden On

I found a way to set up NSCollectionView without needing to add a .xib file.

The trick was inheriting from NSCollectionViewItem and using that subclass for -[NSCollectionView registerClass:forItemWithIdentifier:].

#import "ViewController.h"

@interface MyViewItem : NSCollectionViewItem
@end

@implementation MyViewItem {
}

- (instancetype)initWithNibName:(NSNibName)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    self = [super initWithNibName:nil bundle:nil];
    
    NSButton *button = [[NSButton alloc] initWithFrame:NSZeroRect];
    [button setTitle:@"Button"];

    [self setView:button];
    
    return self;
}

@end

@implementation ViewController {
    IBOutlet NSCollectionView *_collectionView;
}

- (void)viewDidLoad {
    [super viewDidLoad];

    [_collectionView registerClass:[MyViewItem class]
             forItemWithIdentifier:@"item"];
    [_collectionView setDataSource:self];
}

- (NSInteger)collectionView:(NSCollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return 10;
}

- (NSCollectionViewItem *)collectionView:(NSCollectionView *)collectionView itemForRepresentedObjectAtIndexPath:(NSIndexPath *)indexPath {
    
    return [_collectionView makeItemWithIdentifier:@"item" forIndexPath:indexPath];
}

@end