Swift中用.map修改collectionView performBatchUpdates的插入indexPath
调整UICollectionView Batch Updates中插入IndexPath的正确姿势
你踩中了UICollectionView batch updates里一个很常见的坑:插入操作的IndexPath是基于删除(或其他修改)操作完成后的数据源状态,而不是原始状态。结合你的场景,我来一步步拆解怎么用.map正确调整插入路径。
先理清楚你的场景逻辑
原始数据源是 [1,2,3,4,5,6,7](对应IndexPath的item为0-6),你要先删除[0,0]和[0,1](也就是元素1和2),删除完成后,数据源变成[3,4,5,6,7],对应的item索引变成0-4。
你原本想插入到原始的[0,3]和[0,4],但这个位置在删除操作后已经不存在了——原始的item3(元素4)现在的索引是1,原始的item4(元素5)现在的索引是2。所以必须把插入路径调整为删除后的对应位置。
用.map计算调整后的插入IndexPath
核心思路是:对每个原始插入路径,减去「在它之前被删除的元素数量」。具体实现可以这样:
// 你的原始删除和插入路径 let deletedIndexPaths: [IndexPath] = [IndexPath(item: 0, section: 0), IndexPath(item: 1, section: 0)] let originalInsertPaths: [IndexPath] = [IndexPath(item: 3, section: 0), IndexPath(item: 4, section: 0)] // 用.map转换插入路径 let adjustedInsertPaths = originalInsertPaths.map { insertPath in // 统计同section下,删除的item小于当前插入item的数量 let deletedCountBefore = deletedIndexPaths .filter { $0.section == insertPath.section && $0.item < insertPath.item } .count // 调整item值:原始item数 - 前面被删除的数量 return IndexPath(item: insertPath.item - deletedCountBefore, section: insertPath.section) }
运行这段代码后,adjustedInsertPaths会变成[IndexPath(item:1, section:0), IndexPath(item:2, section:0)],这才是删除操作完成后正确的插入位置。
完整的Batch Updates示例
别忘了,数据源的更新和CollectionView的操作必须严格同步,顺序也要对应:
// 假设你的数据源是一个数组 var dataSource: [Int] = [1,2,3,4,5,6,7] let newItems = [8,9] // 要插入的新元素 collectionView.performBatchUpdates { // 1. 先更新数据源的删除操作(倒序删除避免索引混乱) let deletedItems = deletedIndexPaths.map { $0.item }.sorted(by: >) deletedItems.forEach { dataSource.remove(at: $0) } // 2. 再更新数据源的插入操作 adjustedInsertPaths.enumerated().forEach { index, path in dataSource.insert(newItems[index], at: path.item) } // 3. 通知CollectionView执行对应的动画 collectionView.deleteItems(at: deletedIndexPaths) collectionView.insertItems(at: adjustedInsertPaths) } completion: { _ in // 操作完成后的回调 }
优化小技巧
如果删除的元素很多,用filter遍历统计数量效率不高,可以先把删除的item排序,再用二分查找快速定位:
// 先把同section的删除item排序 let deletedItemsInSection = deletedIndexPaths .filter { $0.section == insertPath.section } .map { $0.item } .sorted() // 用二分查找找到第一个大于等于插入item的位置,这个位置的索引就是前面被删除的数量 let deletedCountBefore = deletedItemsInSection.partition { $0 < insertPath.item }
这样能把时间复杂度从O(n)降到O(log n),数据量大的时候更高效。
内容的提问来源于stack exchange,提问作者lozflan
相关产品推荐
相关产品推荐

