問題:collection view 在reloadData之后,找不到 cell
今天碰到的一個問題:app 主界面是一個 collection view,當數據源增加一個數據時,我需要立即刷新視圖,并打開相應的 cell。
于是我寫了如下代碼,
[self.collectionView reloadData];
UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath];
[self performSegueWithIdentifier:openInFileIdentifier sender:cell];
debug 發現獲取的 cell 是 nil,在斷點處查看 collection view 的 visibleCells 也是空的。我猜測 reloadData 背后開啟了分線程來處理這個事情,所以 reloadData 方法返回的時候,視圖并沒有完成刷新。
解決方案
去 stackOverFlow 上搜索一下,找到了一個解決方案:
UICollectionView 有一個方法,
- (void)performBatchUpdates:(void (^)(void))updates completion:(void (^)(BOOL finished))completion; // allows multiple insert/delete/reload/move calls to be animated simultaneously. Nestable.
這個方法會在第一個 block 中處理 insert/delete/reload/move 等操作,等操作完成之后會執行第二個block。更新代碼,問題解決。
[self.collectionView reloadData];
[self.collectionView performBatchUpdates:^{
[self.collectionView reloadItemsAtIndexPaths:@[indexPath]];
} completion:^(BOOL finished) {
UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath];
[self performSegueWithIdentifier:openInFileIdentifier sender:cell];
}];
如果 cell 不在視圖內呢?
如果 cell 剛好不在屏幕區域內,處于 visible 的狀態,那么用上述方法依然會找不到 cell。我想可以先讓 collection view 滾動到目標 cell 處,然后在打開 cell。這里需要用到 scroll 的代理方法,以在滾動動畫結束的時候,找到目標 cell。(滾動結束才能確定目標 cell 是 visible 的。)你還需要定義一個 indexPath 值以區分滾動結束的時候是否需要打開cell,以及要打開哪個 cell。代碼如下,
NSIndexPath *needOpenCellIndexPath;
- (void)foo
{
needOpenCellIndexPath = ...;
[self.collectionView performBatchUpdates:^{
[self.collectionView reloadItemsAtIndexPaths:@[needOpenCellIndexPath]];
} completion:^(BOOL finished) {
UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:needOpenCellIndexPath];
if (cell) {
[self performSegueWithIdentifier:RPAOpenMapSegueIdentifier sender:cell];
} else {
// Start scrolling to the target cell.
[self.collectionView selectItemAtIndexPath:needOpenCellIndexPath animated:YES scrollPosition:UICollectionViewScrollPositionTop];
}
}];
}
#pragma mark - scroll view deleagte
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
if (needOpenCellIndexPath) {
UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:needOpenCellIndexPath];
[self performSegueWithIdentifier:openInFileIdentifier sender:cell];
}
needOpenCellIndexPath = nil;
}
#pragma mark -
測試,問題完美解決。
歡迎來我的個站逛逛: http://alexyu.me/