3

I'm trying to set up an NSCollectionView with a flow layout in vertical orientation. To quickly test the data source, I am returning static numbers and views from collectionView(_:numberOfItemsInSection:) and collectionView(_:itemForRepresentedObjectAtIndexPath:) respectively.

Here are the methods:

func collectionView(collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
  return 2
}

func collectionView(collectionView: NSCollectionView,
                    itemForRepresentedObjectAtIndexPath indexPath: NSIndexPath) -> NSCollectionViewItem {
  let item = NSCollectionViewItem()
  let view = NSView()
  item.view = view
  view.wantsLayer = true
  view.layer?.backgroundColor = CGColorCreateGenericGray(0, 0.5)
  view.translatesAutoresizingMaskIntoConstraints = false
  view.widthAnchor.constraintEqualToConstant(100).active = true
  view.heightAnchor.constraintEqualToConstant(100).active = true
  return item
}

Basically, I always return 2 NSCollectionViewItems that are all black with 50% opacity. When I run the app, it placed both views on top of each other.

app screenshot showing a single dark gray square

Here's an exploded showing both views:

enter image description here

Each item is set to have a size of 50x50, and minimum spacing of 10 in each direction.

What am I missing? What makes these views be spaced apart? In the documentation, it says:

Normally, you specify the size of items and their spacing using the properties of this class. If you want to customize the values for your items, implement the methods of the NSCollectionViewDelegateFlowLayout protocol in the object assigned as the collection view’s delegate.

I'm not implementing this delegate, so shouldn't it work with the values I put in interface builder (50x50 and 10 spacing)?

Nirav D
  • 71,513
  • 12
  • 161
  • 183
Edward Loveall
  • 1,983
  • 1
  • 19
  • 34
  • also, how did you do the exploded? that is really cool... though it doesn't help my problem of overlapping – tofutim Sep 23 '17 at 09:48
  • It's called Debug View Hierarchy. Check this out: https://stackoverflow.com/questions/28657634/xcode-cant-find-debug-view-hierarchy-button#28658019 – Edward Loveall Sep 23 '17 at 14:10

2 Answers2

0

I'm still not sure exactly what the problem was, but here's what I did to fix it:

  1. Create a NSCollectionViewItem subclass with associated xib (let's call the subclass Item)
  2. Add a Collection View Item to the Document Outline in interface builder
  3. Change the class of the Collection View Item to my custom subclass (Item)
  4. Change the code in collectionView(_:itemForRepresentedObjectAtIndexPath:) to the following:

func collectionView(collectionView: NSCollectionView,
                    itemForRepresentedObjectAtIndexPath indexPath: NSIndexPath) -> NSCollectionViewItem {
  return collectionView.makeItemWithIdentifier("Item", forIndexPath: indexPath)
}

Now I not only have views laying out as they should, but they are custom and not just gray boxes! Following this tutorial helped me solve the problem.

If anyone wants to take a stab at explaining what the actual problem was, I'll leave this open and accept something that actually answers the question.

Edward Loveall
  • 1,983
  • 1
  • 19
  • 34
0

I've run into the same problem and, assuming it's the same thing affecting you, have also figured out the fix.

The symptoms described occur when more than one instance of an NSCollectionViewItem subclass ends up configured to target the same item view. The way I ran into this was pretty simple, as I was trying to set up a class-based item, that was nonetheless backed by a xib. Something like this:

  1. Create a new NSCollectionViewItem subclass, specifying a nibName and nibBundle.
  2. Create a new NSView nib.
  3. Drag a collection view instance out, and connect its outlets to the view.
  4. Set the File's Owner class to your subclass name.
  5. Drag outlet connections from the File's Owner proxy to the views.
  6. In code, use NSCollectionView.register(class:...) to set up the item.

Now you've got a class that can be instantiated and self-load its nib, thanks to the default NSViewController machinery. But, when you do, it will also instantiate an additional NSCollectionViewItem instance that also points at the view.

My theory is the second, bogus view item instance ends up getting consulted for view positioning when re-laying-out, and that it defaults to 0-0 index path, thus causing each associated view to be drawn in the same position.

The fix is to remove the bogus top level view item in the xib, or else switch to using the register(nib:...) based method.

danielpunkass
  • 17,527
  • 4
  • 24
  • 38