guiaume
guiaume

Reputation: 77

CollectionView scroll to newly inserted item

I've got a CollectionView driven by a NSFetchedResultsController.

The CollectionViewLayout is an horizontal "carousel" layout of cells ordered by ascending names.

New items are inserted with customs animations.

 func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {

if type == NSFetchedResultsChangeType.Insert {
    println("Insert Object: \(newIndexPath)")

   UIView.animate(withDuration: 1.0, delay: 0.0, options: .curveEaseOut, animations: {
       self.collectionView?.insertItems(at: [newIndexPath])
   }, completion: { finished in
       self.collectionView?.scrollToItem(at: newIndexPath, at: .centeredHorizontally, animated: true)
})
    )
...

It works but the animation is kind of glitchy and the scroll happening at the same time.

I would like to scroll to Item then insert it with custom animation but if I scroll to Item before inserting it the app crash.

What is the right thing to do here?

Thanks

Upvotes: 2

Views: 1785

Answers (2)

Jeffery Thomas
Jeffery Thomas

Reputation: 42588

The first thing I would try is replace animate(withDuration:delay:options:animations:completion:) with performBatchUpdates(_:completion:)

func controller(controller: NSFetchedResultsController,
                didChangeObject anObject: AnyObject,
                atIndexPath indexPath: NSIndexPath?,
                forChangeType type: NSFetchedResultsChangeType,
                newIndexPath: NSIndexPath?)
{

    if type == NSFetchedResultsChangeType.Insert {
        println("Insert Object: \(newIndexPath)")

    self.collectionView?.performBatchUpdates({
        self.collectionView?.insertItems(at: [newIndexPath])
    }, completion: { finished in
        self.collectionView?.scrollToItem(at: newIndexPath, at: .centeredHorizontally, animated: true)
    })
    …
}

If that is still giving you problems, then you can call the scroll in the next run loop.

func controller(controller: NSFetchedResultsController,
                didChangeObject anObject: AnyObject,
                atIndexPath indexPath: NSIndexPath?,
                forChangeType type: NSFetchedResultsChangeType,
                newIndexPath: NSIndexPath?)
{

    if type == NSFetchedResultsChangeType.Insert {
        println("Insert Object: \(newIndexPath)")

    self.collectionView?.performBatchUpdates({
        self.collectionView?.insertItems(at: [newIndexPath])
    }, completion: { finished in
        DispatchQueue.main.async { // Defer to next runlop.
            self.collectionView?.scrollToItem(at: newIndexPath, at: .centeredHorizontally, animated: true)
        }
    })
    …
}

Finally, you could try only animating to scroll part.

func controller(controller: NSFetchedResultsController,
                didChangeObject anObject: AnyObject,
                atIndexPath indexPath: NSIndexPath?,
                forChangeType type: NSFetchedResultsChangeType,
                newIndexPath: NSIndexPath?)
{

    if type == NSFetchedResultsChangeType.Insert {
        println("Insert Object: \(newIndexPath)")

    self.collectionView?.reloadItems(at: [newIndexPath]) // reload without animating.

    DispatchQueue.main.async { // Defer to next runlop.
        self.collectionView?.scrollToItem(at: newIndexPath, at: .centeredHorizontally, animated: true)
    }
    …
}

Upvotes: 1

ScenicJaguar101
ScenicJaguar101

Reputation: 1

From looking at the function, I can imagine that the glitch you see is the CollectionView scrolling forward then rubberbanding/"teleporting" back.

I think this might be happening because you're trying to run two animations at the same time. Your first animation, UIView.animate(), animates the scrolling to the new inserted item; however, when you call collectionView?.scrollToItem(), you're animating that as well (see below)

self.collectionView?.scrollToItem(..., animated: true) <--

That is probably why it's not working; you're trying to animate an animation. Try setting animated to false and see if that fixes it.

Upvotes: 0

Related Questions