I am developing a macOS app targeting macOS 10.10 SDK and using Xcode 9.3 (Swift 4). I am not using xibs, but creating all views programmatically.
I want to create a NSCollectionView
. I register a subclass of NSCollectionViewItem
and then register that class to the NSCollectionView
with a call to collectionView.register(:,forItemWithIdentifier)
. Later, in the data source, I call collectionView.makeItem(withIdentifier:,for:)
.
However the makeItem
method always returns nil. What am I doing wrong?
I found a similar question but the solution is to call register
, which I already do.
For reference, here's the minimum working to reproduce my the issue: when I put a breakpoint after the call to makeItem
, I can see that the returned value is always nil
.
import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
@IBOutlet weak var window: NSWindow!
func applicationDidFinishLaunching(_ aNotification: Notification) {
window.contentViewController = TestViewController()
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}
let cellIdentifier = NSUserInterfaceItemIdentifier(rawValue: "testIdentifier")
class TestViewController: NSViewController, NSCollectionViewDataSource {
override func loadView() {
self.view = NSView()
}
override func viewDidLoad() {
let scroll = NSScrollView()
self.view.addSubview(scroll)
scroll.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint(item: scroll, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: scroll, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: scroll, attribute: .left, relatedBy: .equal, toItem: self.view, attribute: .left, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: scroll, attribute: .right, relatedBy: .equal, toItem: self.view, attribute: .right, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: scroll, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 500).isActive = true
NSLayoutConstraint(item: scroll, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 500).isActive = true
let collection = NSCollectionView()
scroll.documentView = collection
collection.register(TestViewItem.self, forItemWithIdentifier: cellIdentifier)
collection.dataSource = self
collection.collectionViewLayout = NSCollectionViewFlowLayout()
collection.reloadData()
}
func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
let item = collectionView.makeItem(withIdentifier: cellIdentifier, for: indexPath)
return item
}
}
class TestViewItem: NSCollectionViewItem {
override func loadView() {
self.view = NSView()
}
}
I think this is a bug. Workaround: set collectionViewLayout
before registering the class.
Apparently the registered class is not stored if collectionViewLayout
is not set. collectionViewLayout
can be set to a new layout after registering the class, the registered class isn't removed.