I am trying to create a simple NSCollectionView
programmatically. I am setting it up in the NSViewController
:
class ViewController: NSViewController {
var scrollView: NSScrollView!
var collectionView: NSCollectionView!
override func loadView() {
self.view = NSView(frame: NSRect(x: 0, y: 0, width: 600, height: 400))
}
override func viewDidLoad() {
super.viewDidLoad()
collectionView = NSCollectionView(frame: NSZeroRect)
collectionView.backgroundView?.wantsLayer = true
collectionView.backgroundColors = [NSColor.black]
collectionView.collectionViewLayout = NSCollectionViewFlowLayout()
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(CollectionViewItem.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier("CollectionViewItem"))
scrollView = NSScrollView(frame: view.frame)
scrollView.documentView = collectionView
view.addSubview(scrollView)
self.scrollView.translatesAutoresizingMaskIntoConstraints = false
self.scrollView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
self.scrollView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
self.scrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
self.scrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
}
}
extension ViewController: NSCollectionViewDataSource {
func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
let item = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier("CollectionViewItem"), for: indexPath)
item.representedObject = indexPath.item
return item
}
}
extension ViewController: NSCollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> NSSize {
return NSSize(width: 25, height: 25)
}
}
Here's how I am setting up the NSCollectionViewItem
:
class CollectionViewItem: NSCollectionViewItem {
var collectionViewItemView: CollectionViewItemView?
override func loadView() {
if (self.representedObject != nil) {
self.collectionViewItemView = CollectionViewItemView(frame: NSZeroRect, number: self.representedObject as! Int)
self.view = collectionViewItemView!
} else {
self.view = CollectionViewItemView(frame: NSZeroRect)
}
}
override var representedObject: Any? {
didSet {
loadView()
}
}
}
And here's how I'm creating a custom NSView
to work inside the NSCollectionViewItem
:
class CollectionViewItemView: NSView {
var number: Int?
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
}
convenience init(frame frameRect: NSRect, number: Int) {
self.init(frame: frameRect)
self.number = number
layoutSubviews()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
func layoutSubviews() {
let label = NSTextField(frame: NSMakeRect(0, 0, 60, 30))
label.stringValue = "\(self.number!)"
addSubview(label)
}
}
Now, despite the fact that I am using an NSCollectionViewFlowLayout
, conforming to the NSCollectionViewDelegateFlowLayout
and providing a sizeForItemAt
all indices, the resultant CollectionViewItemView
is always sized 0, as shown below:
In sample projects online, the authors always feed an NSZeroRect
to the frame of the NSCollectionViewItem
. If I give it a custom NSRect
, it always renders them on top of each other (since the origin of the frame is always the same).
What is the proper way of doing this? I'm at a loss. Why is the NSCollectionView
not responding to the NSCollectionViewDelegateFlowLayout
?
I've never seen this problem, but here's a guess:
In CollectionViewItem
you are overriding loadView()
but it never calls super.loadView()
. CollectionViewItem
is a subclass of NSViewController
and if you override loadView()
you must first invoke super.loadView()
so the view controller can initialize its internal state.
If you're going to be targeting macOS 10.10 and later, you probably want to override viewDidLoad()
instead.