0

I have a Storyboard that contains a controller with a NSCollectionView. The Storyboard also contains a NSCollectionViewItem with outlets connected to NSCollectionViewItem subclass, FeedItem.

enter image description here

How do you use the the "cell" or "scene" from the Storyboard (not a separate nib file) in the itemForRepresentedObjectAt method?

This is what I have so far..

  class FeedsController : NSViewController, NSCollectionViewDataSource {
  @IBOutlet weak var collectionView: NSCollectionView!

  override func viewDidLoad() {
    super.viewDidLoad()
  }

  func numberOfSections(in collectionView: NSCollectionView) -> Int {
    return 1
  }

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

  func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
    let collectionViewItem = self.storyboard?.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "FeedItem")) as! FeedItem

    collectionViewItem.urlLabel.stringValue = "http://www.seanbehan.com"
    collectionViewItem.entriesLabel.stringValue = "15 Entries"
    collectionViewItem.updatedLabel.stringValue = "Last Update: June 1st at 3:15PM"

    return collectionViewItem
  }
}

class FeedItem : NSCollectionViewItem {
  @IBOutlet weak var urlLabel: NSTextField!
  @IBOutlet weak var updatedLabel: NSTextField!
  @IBOutlet weak var entriesLabel: NSTextField!
}

It is a MacOS Cocoa app using Swift 4.1

seanbehan
  • 1,463
  • 15
  • 23

3 Answers3

1

The only way to instantiate NSCollectionViewItem from storyboard is itemProtorype which is deprecated at 10.14

If you still using an older SDK, you could do so

let itemIdentifier = NSStoryboard.SceneIdentifier(rawValue: "CollectionViewItemIdentifier")

if let item = NSStoryboard.main?.instantiateController(withIdentifier: itemIdentifier) as? NSCollectionViewItem {
    collectionView.itemPrototype = item
}

But I highly recommend to use .xib and methods described by Apple documentation:

Long story shorts you have to use .xib file for your NSCollectionViewItem

Community
  • 1
  • 1
ailinykh
  • 73
  • 1
  • 5
1

Storyboard Option

If you want to include the NSCollectionViewItem in the same storyboard as your NSCollectionView

  • Using the Library (plus symbol, upper right, cmd-shift-L), drag a NSCollectionViewItem onto (and empty area of) the canvas.
  • Using the Document Outline (left of canvas, menu: Editor->Document Outline), select the "Collection View Item" in the "Collection View Item Scene".
  • Using the Identity Inspector (right of canvas, cmd-option-4) assign the "Storyboard ID" to "MyCustomItem".
  • Optional: for reasonably complex items or for maintainability reasons, you may want to change the "Class" to a custom subclass.
  • It's non-intuitive, but NSCollectionViewItem is a subclass of NSViewController. In your implementation of NSCollectionViewDataSource implement…
    func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
        guard let item = self.storyboard?.instantiateController(withIdentifier: "MyCustomItem") as? NSCollectionViewItem else {
            return NSCollectionViewItem()
        }
        // …
        return item
    }

note: subclasses will want to change as? downcast class to their subclass

There is no need to use any of the register methods for this to work.

bshirley
  • 8,217
  • 1
  • 37
  • 43
0

The docs are always worth glancing at:

You do this by calling the makeItem(withIdentifier:for:) method of the collection view to retrieve an empty item object of the appropriate type.

And as the docs further explain, to do that you will need to have registered a nib or a class with the collection view.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Clearly neither the nib nor the class is going to hand you the view item that can be instantiated in the storyboard, so Don't Do That. – matt Jun 15 '18 at 19:14
  • Thanks for the input. But I read the docs. I initially had this.. code `collectionView.register(FeedItem.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier("FeedItem"))` and `let collectionViewItem = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier("FeedItem"), for: indexPath) as! FeedItem` ` but it throws an exception ` `-[NSNib _initWithNibNamed:bundle:options:] could not load the nibName: FeedItem in bundle (null).` – seanbehan Jun 15 '18 at 20:18
  • So shouldn't the question be about why that code didn't work? – matt Jun 15 '18 at 20:58
  • I'm not using a nib file. I want to grab the view from the storyboard w/out using a nib file. – seanbehan Jun 15 '18 at 21:01
  • To me it seems odd that you can't use a storyboard for the UI of a collection view item and would be required to use a nib. – seanbehan Jun 15 '18 at 21:07
  • And I'm saying you can't. Unlike iOS collection view cells, there are no collection view item prototypes in the storyboard on macOS. So you can't get a reusable collection view item that way. – matt Jun 15 '18 at 21:07
  • Of course it's odd. But this is what happens when iOS is slowly read back onto macOS. It's like the weird way view controllers or CALayer-backed views has gotten folded in. – matt Jun 15 '18 at 21:08