Reputation: 77
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
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
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