33

我有一个UICollectionView我正在尝试动态/使用动画将项目插入其中。所以我有一些功能可以异步下载图像并希望批量插入项目。

获得数据后,我想执行以下操作:

[self.collectionView performBatchUpdates:^{
    for (UIImage *image in images) {
        [self.collectionView insertItemsAtIndexPaths:****]
    }
} completion:nil];

现在代替***,我应该传递一个 数组NSIndexPaths,它应该指向要插入的新项目的位置。我很困惑,因为在提供位置后,我如何提供应该在该位置显示的实际图像?

谢谢


更新:

resultsSize包含数据源数组的大小,self.results在从 处的数据添加新数据之前newImages

[self.collectionView performBatchUpdates:^{

    int resultsSize = [self.results count];
    [self.results addObjectsFromArray:newImages];
    NSMutableArray *arrayWithIndexPaths = [NSMutableArray array];

    for (int i = resultsSize; i < resultsSize + newImages.count; i++)
          [arrayWithIndexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]];

          [self.collectionView insertItemsAtIndexPaths:arrayWithIndexPaths];

} completion:nil];
4

3 回答 3

34

请参阅“ iOS 集合视图编程指南”中的插入、删除和移动部分和项目

要插入、删除或移动单个部分或项目,您必须执行以下步骤:

  1. 更新数据源对象中的数据。
  2. 调用集合视图的适当方法来插入或删除部分或项目。

在通知集合视图任何更改之前更新数据源至关重要。集合视图方法假定您的数据源包含当前正确的数据。如果没有,collection view 可能会从您的数据源接收到错误的项目集,或者请求不存在的项目并使您的应用程序崩溃。

因此,在您的情况下,您必须先将图像添加到集合视图数据源,然后再调用insertItemsAtIndexPaths. 然后,集合视图将要求数据源委托函数为插入的项目提供视图。

于 2012-09-29T21:48:52.197 回答
12

我刚刚用 Swift 实现了它。所以我想分享我的实现。首先初始化一个 NSBlockOperations 数组:

    var blockOperations: [NSBlockOperation] = []

在控制器将更改,重新初始化数组:

func controllerWillChangeContent(controller: NSFetchedResultsController) {
    blockOperations.removeAll(keepCapacity: false)
}

在确实更改对象方​​法中:

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

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

        blockOperations.append(
            NSBlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.insertItemsAtIndexPaths([newIndexPath!])
                }
            })
        )
    }
    else if type == NSFetchedResultsChangeType.Update {
        println("Update Object: \(indexPath)")
        blockOperations.append(
            NSBlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.reloadItemsAtIndexPaths([indexPath!])
                }
            })
        )
    }
    else if type == NSFetchedResultsChangeType.Move {
        println("Move Object: \(indexPath)")

        blockOperations.append(
            NSBlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.moveItemAtIndexPath(indexPath!, toIndexPath: newIndexPath!)
                }
            })
        )
    }
    else if type == NSFetchedResultsChangeType.Delete {
        println("Delete Object: \(indexPath)")

        blockOperations.append(
            NSBlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.deleteItemsAtIndexPaths([indexPath!])
                }
            })
        )
    }
}

在确实更改部分方法中:

func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {

    if type == NSFetchedResultsChangeType.Insert {
        println("Insert Section: \(sectionIndex)")

        blockOperations.append(
            NSBlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.insertSections(NSIndexSet(index: sectionIndex))
                }
            })
        )
    }
    else if type == NSFetchedResultsChangeType.Update {
        println("Update Section: \(sectionIndex)")
        blockOperations.append(
            NSBlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.reloadSections(NSIndexSet(index: sectionIndex))
                }
            })
        )
    }
    else if type == NSFetchedResultsChangeType.Delete {
        println("Delete Section: \(sectionIndex)")

        blockOperations.append(
            NSBlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.deleteSections(NSIndexSet(index: sectionIndex))
                }
            })
        )
    }
}

最后,在 did 控制器中确实更改了内容方法:

func controllerDidChangeContent(controller: NSFetchedResultsController) {        
    collectionView!.performBatchUpdates({ () -> Void in
        for operation: NSBlockOperation in self.blockOperations {
            operation.start()
        }
    }, completion: { (finished) -> Void in
        self.blockOperations.removeAll(keepCapacity: false)
    })
}

我个人也在 deinit 方法中添加了一些代码,以便在 ViewController 即将被释放时取消操作:

deinit {
    // Cancel all block operations when VC deallocates
    for operation: NSBlockOperation in blockOperations {
        operation.cancel()
    }

    blockOperations.removeAll(keepCapacity: false)
}
于 2015-03-05T12:45:16.413 回答
5

从索引中删除项目时我遇到了类似的问题,这是我认为我们在使用performBatchUpdates:方法时需要做的事情。

1# 首先调用 deleteItemAtIndexPath 从集合视图中删除项目。

2# 从数组中删除元素。

3# 通过重新加载数据来更新集合视图。

[self.collectionView performBatchUpdates:^{
            NSIndexPath *indexPath = [NSIndexPath indexPathForRow:sender.tag inSection:0];
            [self.collectionView deleteItemsAtIndexPaths:[NSArray arrayWithObject:indexPath]];
            [self.addNewDocumentArray removeObjectAtIndex:sender.tag];
        } completion:^(BOOL finished) {
            [self.collectionView reloadData];
        }];

这有助于我消除所有崩溃和断言失败。

于 2016-06-10T11:05:43.400 回答