NSCollectionView sizeForItemAt always returning exception when trying to reference an item in its collection

走远了吗. 提交于 2020-02-24 11:25:30

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!