4
votes

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?

1
Here are my thoughts. No. 1, you should state the version of Xcode you are using. It appears that you are using Xcode 7 or lower. There have been changes with the itemForRepresentedObjectAt method since introduction of Xcode 8. No. 2, identifiier is the name of the NSCollectionViewItem xib file. So setting 'Image' to the identifier does not make sense. No. 3, you should just create a collectionitem Xib file except that. It was terribly difficult when I did it in Swift some 3 to 4 weeks ago since information is limited for Swift.El Tomato
1.) I'm using Xcode 8.2 on macOS 10.11.6. 2) As I mentioned, I don't have an NSCollectionViewItem xib file, so there's no identifier I can give for a .xib file. Also, the docs say you can register a class instead of a .xib file, so that's probably what I'd like to do (and tried to do as you can see above). 3) I don't understand this sentence, so I don't have a response.user1118321
Oops... I was going to write 'except that it was terribly...' As far as I know, the makeItemWithIdentifier method has been renamed makeItem since Xcode 8. But if you say you are using Xcode 8, I guess you are using Xcode 8. Well, good luck...El Tomato
I believe it's the following topic that I used as a reference when I tried to do the same (without using a xib file for collection item) a few years ago. stackoverflow.com/questions/8660626/… See if it's helpful.El Tomato
Thanks for the link. However, in the answer, they subclass NSCollectionViewItem which I am trying to avoid. The docs say you don't have to subclass it, and I'd like not to.user1118321

1 Answers

3
votes

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.