Giles
Giles

Reputation: 1667

No Animations on NSCollectionView with NSCollectionViewFlowLayout

I am programmatically setting up a simple collection view but not seeing the insert/remove animations that I expected to get for free. I have checked out all methods on the classes associated with collection views and can't find anything that would seem to affect the animation. Is there something I am missing in this set-up please?

When inserting something at index 0 I am expecting to see the existing items move and a new item fading in.

class ECTestViewController: NSViewController
{
    var scrollView: NSScrollView!
    var collectionView: NSCollectionView!

    var items = [Int]()

    override func loadView() {
        view = NSView()
        view.translatesAutoresizingMaskIntoConstraints = false
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        scrollView = NSScrollView()
        scrollView.translatesAutoresizingMaskIntoConstraints = false

        view.addSubview(scrollView)
        let scrollViewList = ["view": scrollView]
        var scrollViewConstraints = NSLayoutConstraint.constraints(withVisualFormat: "|[view]|", metrics: nil, views: scrollViewList)
        scrollViewConstraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|-22-[view]-8-|", metrics: nil, views: scrollViewList)
        NSLayoutConstraint.activate(scrollViewConstraints)

        collectionView = NSCollectionView()
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.dataSource = self
        collectionView.delegate = self
        scrollView.documentView = collectionView

        collectionView.collectionViewLayout = NSCollectionViewFlowLayout()

        // Register the item types for the collection
        collectionView.register(ECTestItem.self, forItemWithIdentifier: ECTestItem.interfaceIdentifier)

        // Add our first item
        items.append(0)

        // Buttons to add and remove items
        let openImage = NSImage(named: NSImage.Name("Open"))
        let openButton = ECImageButton(image: openImage!)
        view.addSubview(openButton)
        openButton.action = #selector(openItem)
        openButton.target = self

        let closeImage = NSImage(named: NSImage.Name("Close"))
        let closeButton = ECImageButton(image: closeImage!)
        view.addSubview(closeButton)
        closeButton.action = #selector(closeItem)
        closeButton.target = self
    }

    @objc func closeItem() {
        items.remove(at: 0)
        collectionView.deleteItems(at: [IndexPath(item: 0, section: 0)])
    }

    @objc func openItem() {
        items.insert(0, at: 0)
        collectionView.insertItems(at: [IndexPath(item: 0, section: 0)])
    }
}

extension ECTestViewController: NSCollectionViewDataSource
{
    func numberOfSections(in collectionView: NSCollectionView) -> Int {
        return 1
    }

    func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
        return items.count
    }

    func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
        return collectionView.makeItem(withIdentifier: ECTestItem.interfaceIdentifier, for: indexPath)
    }
}

extension ECTestViewController: NSCollectionViewDelegateFlowLayout // Actually set as the collection view delegate
{
    func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> NSSize {
        return NSMakeSize(100, 100)
    }
}

class ECTestItem: NSCollectionViewItem
{
    static var interfaceIdentifier: NSUserInterfaceItemIdentifier { get { return NSUserInterfaceItemIdentifier("ouliner") } }

    override func loadView() {
        view = NSView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.wantsLayer = true
        view.layer?.backgroundColor = CGColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

Upvotes: 1

Views: 1012

Answers (1)

Giles
Giles

Reputation: 1667

For changes to be animated, methods need to be called through the animator proxy. Making the change below gave the animations that I expected.

Incorrect:

collectionView.insertItems(at: [IndexPath(item: 0, section: 0)])

Correct:

collectionView.animator().insertItems(at: [IndexPath(item: 0, section: 0)])

This was not a concept that I was aware of before, and there is no mention of it in the collection view animation documentation.

Upvotes: 7

Related Questions