Context -
I am working on a macOS menu bar app which displays a collection of items. The items exist in a collection view and contain a textfield.
Right now, the collection view layout is at a fixed height of 50, and I am trying to dynamically increase the size of the cell based on the size of the textfield.
I believe the best way to do this is in the sizeForItemAt
method, but I can not seem to reference a cell and its textfield to calculate and return it's height. Whenever I try and get a collectiomView item at this point, the app crashes with an [General] *** -[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]
exception.
My Code -
My viewDidLoad, where I 'configure' my CollectionView
override func viewDidLoad() {
super.viewDidLoad()
configureCollectionView()
....
}
private func configureCollectionView() {
let flowLayout = NSCollectionViewFlowLayout()
flowLayout.itemSize.width = self.view.frame.width
flowLayout.minimumLineSpacing = 8
thoughtsCollectionView.collectionViewLayout = flowLayout
view.wantsLayer = true
}
This is where Im trying to set the height dynamically (and where it crashes)
func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> NSSize {
guard collectionView.item(at: indexPath) != nil else { // <-- CRASHES HERE
return NSSize(width: self.view.frame.width, height: 50)
}
return self.collectionView.item(at: indexPath)?.textField?.frame.height
}
This is how Im adding items to the collectionView
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
let item = collectionView.makeItem(withIdentifier: "CollectionViewItem", for: indexPath as IndexPath)
guard let collectionViewItem = item as? CollectionViewItem else {return item}
(item as! CollectionViewItem).textField?.stringValue = "Some Text"
(item as! CollectionViewItem).delegate = self
return item
}
So what is strange to me, is that in my collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set<IndexPath>)
I am to successfully get the item by doing
func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set<IndexPath>) {
guard let indexPath = indexPaths.first else {
return
}
guard collectionView.item(at: indexPath) != nil else {
return
}
let item = collectionView.item(at: indexPath) as! CollectionViewItem // <--- This gets me an item fine
So I'm unsure why I'm struggling with sizeForItemAt
. I have a feeling its because the items don't exist yet (and are created when the view starts to display them), but unsure how to then correctly approach this dynamic sizing.
I encounter the same issue. With reading the documents, I find that Apple intents to let us use the
NSCollectionViewLayout
instead of the item.If you don't know how to deal with
NSCollectionViewLayout
, you can try to use your data model to regenerate the views and calculate.For example, if you have one image, one mutable size label and one fixed size label. like: Image | Mutable Label | Label
You can regenerate the mutable label like this.