问题
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.
回答1:
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.
func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> NSSize {
let imageViewWidth:CGFloat = 20 + 64 + 20
let account = self.dataObject[indexPath]
let defaultLabelWidth:CGFloat = {
if account.isDefault {
let defaultLabel = NSTextField(labelWithString: NSLocalizedString("Default", comment: ""))
defaultLabel.font = NSFont.systemFont(ofSize: 16.0)
defaultLabel.sizeToFit()
return 20 + defaultLabel.bounds.width + 20
}
return 0 + 0 + 20
}()
let nameLabelWidth:CGFloat = {
let label = NSTextField(labelWithString: account.name)
label.font = NSFont.systemFont(ofSize: 16.0)
label.sizeToFit()
return label.bounds.width
}()
return NSSize(width: imageViewWidth + nameLabelWidth + defaultLabelWidth, height: 80)
}
来源:https://stackoverflow.com/questions/46286020/nscollectionview-sizeforitemat-always-returning-exception-when-trying-to-referen