21

When you drag a NSCollectionView to a view, a NSCollectionViewItem appears on the storyboard, floating around.

Imagine I drag several NScollectionViews to the same view. I will have a bunch of NSCollectionViewItems. How a collection view knows which NScollectionViewItem belongs to it? Is there a connection between the two that can be seen on interface builder? I don't see anything on interface builder? Where to do I see that?

EDIT: Apparently this seems to be a Xcode bug. When you add a NSCollectionView to the storyboard, it comes without a link to the NSCollectionViewItem and it seems to be impossible to connect the itemPrototype outlet between them.

After contacting Apple about bugs like this, their answer was: "this is a know issue with Storyboards on OS X. Use Xibs instead."

idmean
  • 14,540
  • 9
  • 54
  • 83
Duck
  • 34,902
  • 47
  • 248
  • 470
  • 1
    Bug still present in Xcode 6.3 6D570 – alecail Apr 12 '15 at 14:25
  • Bug still present in Xcode 7 Beta 6 – gbdavid Aug 26 '15 at 10:37
  • In Xcode 7.1, there is a segue that shows the relation even when using Storyboards... but when compiling it fails with "Unknown segue relationship: Prototype" – Etan Oct 31 '15 at 11:24
  • Bug still present in Xcode 8.2.1 – Clifton Labrum Feb 04 '17 at 21:39
  • 1
    @CliftonLabrum - knowing Apple this will never be fixed. – Duck Feb 05 '17 at 09:20
  • Bug fixed in Xcode 9! (Also, the default layout is no longer 'legacy'. Hurray.) – green_knight Jun 20 '17 at 22:40
  • @green_knight - wow, they took 3 years to fix that! WTF Apple! The problem is that until we support old versions of MacOS storyboard cannot be used. If this solution is true, we will be able to use storyboards for Mac apps in 3 years. – Duck Jun 21 '17 at 10:41
  • 1
    @green_knight I just tried to connect the `itemPrototype` reference outlet of an `NSCollectionView` to an `NSCollectionViewItem` sitting next to it in the same storyboard, and it won't connect. How did you do it? – Clifton Labrum Sep 30 '17 at 00:08
  • @green_knight I'd like to know this as well! I can't seem to actually get the two to connect. – Ben Kreeger Nov 11 '17 at 22:39
  • 1
    apparently Apple don't give a crap about fixing this 3 years old bug. – Duck Nov 12 '17 at 00:00

8 Answers8

21

Well, I gave @RubberDuck's workaround a go but it didn't work (see my comment). What worked for me is setting collectionView.itemPrototype in viewDidLoad of the view controller (Xcode 6.1):

@IBOutlet weak var collectionView: NSCollectionView!

override func viewDidLoad() {
    // don't forget to set identifier of CollectionViewItem 
    // from interface builder
    let itemPrototype = self.storyboard?.instantiateControllerWithIdentifier("collectionViewItem")
        as NSCollectionViewItem
    self.collectionView.itemPrototype = itemPrototype
}
Community
  • 1
  • 1
mostruash
  • 4,169
  • 1
  • 23
  • 40
  • 1
    i am facing this exact issue. but this solution doesn't seem to work for me. my project is in objective-c and i am setting the itemPrototype property right after `[super viewDidLoad]` but the error is logged even before the code enters the controller's viewDidLoad method. – ashutosh Nov 11 '14 at 14:55
  • @ashutosh I guess we need more information for your case. Did you make sure that the window collection item in your xib has the id `windowCollectionItem`? Edit: Try doing it before `[super viewDidLoad]`. – mostruash Nov 11 '14 at 14:58
  • 1
    @mostruash yes, i have verified that the id is correct. `[self.storyboard instantiateControllerWithIdentifier:@"windowCollectionItem"]` returns non-nil value. – ashutosh Nov 11 '14 at 15:00
  • @mostruash - tried before `[super viewDidLoad]' too. still the same error. – ashutosh Nov 11 '14 at 15:01
  • Hmm. There's something weird there. It shouldn't throw an error before `viewDidLoad` is called. Can you tell us the error that you're getting? – mostruash Nov 11 '14 at 15:02
  • 1
    The complete error message is "Failed to set (contentViewController) user defined inspected property on (NSWindow): NSCollectionView item prototype must not be nil." The storyboard looks like [link](http://cl.ly/image/1f2t1g2j2P0u) – ashutosh Nov 11 '14 at 15:06
  • What is that `contentViewController`? You might be overriding the wrong `viewDidLoad`. You should create a new question and tell more about your problem. I guess you defined that property and bound it to some NSViewController object in the xib but I can't tell without more info. – mostruash Nov 11 '14 at 15:30
  • I checked your storyboard, you should probably subclass `NSWindowController`, set your window controller's class in the xib and override `windowDidLoad` in that class. But I can't tell for sure. Let us know if you solve it! – mostruash Nov 11 '14 at 15:40
  • ok, i solved the issue. the code above works fine. the error i kept getting after adding the above code was due to an extra connection from my array controller to the collection view. – ashutosh Nov 11 '14 at 15:49
  • when I tried this solution, the views loaded, but none of the outlets on the target `NSCollectionViewItem` got bound. I had to reassign them on `representedObject.didSet` – AJ Venturella May 08 '15 at 14:53
  • @AdamVenturella Maybe things changed since XCode 6.1? What version are you on? – mostruash May 08 '15 at 15:15
  • This buggy thing is that we assume the CollectionView and auto-added ViewItem is by default connected. But in fact they are not. – strongwillow Jun 09 '15 at 09:21
  • I am a little late to this question but since it pointed me in the right direction, I thought I would add how I got it working. I am a first time Macos dev using Xamarin. I managed to get this working without using an xib by setting the Storyboard Id on the CollectionItemView and using that id in the Storyboard.initiateControllerWithIdentifier method. See http://andrewteichblog.azurewebsites.net/nscollection-view-bug-in-xcode-6/ – Mark Travis Dec 23 '16 at 00:32
4

Yes, after hours struggling I confirm this one more of Xcode bugs.

The only solution is to edit the file Main.storyboard and add this line to the end of CollectionView section, just before </collectionView>:

<connections>
  <outlet property="itemPrototype" destination="XXXXXXX" id="Kaa-2J-b4e"/>
</connections>

where XXXXXX is the id or the CollectionViewItem. The other number and you can keep the one I post, unless this id is already used by your project, but the chances of this happens is very dim.

Duck
  • 34,902
  • 47
  • 248
  • 470
  • Although this workaround connects `itemPrototype` to `CollectionViewItem` in the storyboard, during runtime I still get the `CollectionView item prototype must not be nil` error. – mostruash Oct 27 '14 at 09:32
  • Yes, I see that too. This is deeper than I thought. I wonder how Apple launches something that half cooked. Thanks. – Duck Oct 27 '14 at 10:53
  • Interesting thing, we have now 2018 and this bug is still alive. But adding outlet property in source code is not enough, you have to also add NSCollectionViewItem to the scene (in source code, because IB creates new scene for Item). – Tomasz Wojtkowiak Apr 25 '18 at 10:16
4

I couldn't get the accepted answer from @mostruash to work in Objective-C either, but I came up with another workaround. Use a custom setter for the collectionView IBOutlet property:

- (void)setCollectionView:(NSCollectionView *)collectionView {
    NSCollectionViewItem *itemPrototype = [self.storyboard instantiateControllerWithIdentifier:@"collection_item"];
    collectionView.itemPrototype = itemPrototype;
    _collectionView = collectionView;
}
mattmcb
  • 41
  • 2
3

Selected answer needs more information:

If you make binding from collectionView's content to array controllers arranged objects using Interface Builder's bindings, it will throw error: "NSCollectionView item prototype must not be nil".

This is because bindings are made before itemPrototype is assigned. Instead, make bindings with code after itemPrototype assignment.

@IBOutlet weak var collectionView: NSCollectionView!
@IBOutlet var arrayController: NSArrayController!
let itemPrototype = self.storyboard?.instantiateControllerWithIdentifier("collectionViewItem")
        as NSCollectionViewItem
self.collectionView.itemPrototype = itemPrototype
collectionView.bind("content", toObject: arrayController, withKeyPath: "arrangedObjects", options: nil)
  • Very first answer and downvote. Guess it's expected. Please consider adding comment if you think this answer can be improved. – hardboiledbaby Jun 22 '15 at 02:01
2

I fall in the same probleam, anyway since 10.11 Apple Documents says:

" You must also disconnect and discard the NSCollectionView’s “itemPrototype”, which is a vestige of the 10.10-and-earlier API model."

from: https://developer.apple.com/library/mac/releasenotes/AppKit/RN-AppKit/#10_11CollectionView

here is explained how it works now.

So we must use old beloved XIBS:

NSCollectionViewItem *item = [collectionView makeItemWithIdentifier:@"Thing" forIndexPath:indexPath];
Or Arbel
  • 2,965
  • 2
  • 30
  • 40
ingconti
  • 10,876
  • 3
  • 61
  • 48
1

The NSCollectionView has an outlet itemPrototype that's connected to its specific NSCollectionViewItem. You can see this in the Connections inspector for either object.

Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • 1
    I see the outlet but it is not connected to anything. Try to drag a collection view to a project and you will see that Xcode does not connect that. To what element should I connect that? It also appears that this outlet don't want to connect to anything. – Duck Oct 24 '14 at 10:00
  • I did try just before posting and I tried again just now. It works for me. Xcode 6.0.1 and 6.1. – Ken Thomases Oct 24 '14 at 10:41
  • can you send me the project, so I can compare mine with yours and see what is wrong? I am using storyboard. Have you tried with storyboards? – Duck Oct 24 '14 at 11:14
  • 5
    I have verified that Xcode lets you connect the outlet if you use Xibs but don't let you with storyboard. – Duck Oct 24 '14 at 11:22
1

Go to the Object Library (the bottom right of Xcode) and search for "Object". It "Provides an instance of an NSObject subclass that is not available in Interface Builder." Drag and drop that into the storyboard, onto the top bar of the ViewController (or alternatively under the ViewController in the tree-view on the LHS.) Then, click on that Object, and in the "Identity Inspector" set its class to be your ViewItem class. Then you can then ctrl+drag from the Collection View to the Object, and it will let you wire it up as "itemPrototype".

Shadowrun
  • 3,572
  • 1
  • 15
  • 13
1

Guys the trick seem to be that you cannot use a regular ViewController from the Object collection in Xcode when using Storyboards.

What works for me is dragging a collection view item from the Objects list and setting its class to your custom class then wiring up all the connections as you normally would to create IBOutlets.

Then you have to instantiate the view instead of makeView but this way allows you to use multiple views in the same collection without registering anything special.

FrostyL
  • 811
  • 6
  • 9