CollectionView 滚动到新插入的项目

CollectionView scroll to newly inserted item

我有一个由 NSFetchedResultsController 驱动的 CollectionView。

CollectionViewLayout 是按名称升序排列的水平 "carousel" 单元格布局。

新项目插入了自定义动画。

 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)
})
    )
...

它可以工作,但动画有点问题,并且滚动是同时发生的。

我想滚动到项目然后用自定义动画插入它,但是如果我在插入它之前滚动到项目应用程序崩溃。

在这里正确的做法是什么?

谢谢

通过查看函数,我可以想象您看到的故障是 CollectionView 向前滚动然后橡皮筋滚动/"teleporting" 向后滚动。

我认为这可能是因为您试图同时 运行 两个动画。 你的第一个动画,UIView.animate(),动画滚动到新插入的项目;但是,当您调用 collectionView?.scrollToItem() 时,您也会对其进行动画处理(见下文)

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

这可能就是它不起作用的原因;您正在尝试制作动画。 尝试将 animated 设置为 false,看看是否可以解决问题。

我要尝试的第一件事是将 animate(withDuration:delay:options:animations:completion:) 替换为 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)
    })
    …
}

如果这仍然给您带来问题,那么您可以在下一个 运行 循环中调用卷轴。

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)
        }
    })
    …
}

最后,您可以尝试只对滚动部分进行动画处理。

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)
    }
    …
}