UICollectionView横向滚动,删除最后一项,animation不能正常工作

我有一个UICollectionView。 它水平滚动,只有一行项目,行为像一个分页UIScrollView。 我正在沿着Safari标签页select器的东西做一些东西,所以你仍然可以看到每个项目的边缘。 我只有一个部分。

如果我删除不是最后一个项目的项目,一切都按预期工作,一个新项目从右侧滑入。

如果我删除最后一个项目,那么集合视图的滚动位置跳转到第N-1个项目(不平滑地animation),然后我看到第N个项目(我删除的那个)淡出。

此行为与我所做的自定义布局无关,因为即使将其切换为使用普通stream布局,也会发生此行为。 我正在删除项目使用:

[self.tabCollectionView deleteItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:index inSection:0]]]; 

有没有人经历过这个? 这是在UICollectionView中的错误,是否有一个解决方法?

我设法让我的实现工作使用标准的UICollectionViewFlowLayout 。 我不得不手动创buildanimation。

首先,我使用基本animation使删除的单元淡出:

 - (void)tappedCloseButtonOnCell:(ScreenCell *)cell { // We don't want to close our last screen. if ([self screenCount] == 1u) { return; } [UIView animateWithDuration:UINavigationControllerHideShowBarDuration animations:^{ // Fade out the cell. cell.alpha = 0.0f; } completion:^(BOOL finished) { NSIndexPath *indexPath = [self.collectionView indexPathForCell:cell]; UIViewController *screen = [self viewControllerAtIndex:indexPath.item]; [self removeScreen:screen animated:YES]; }]; } 

接下来,我导致collections视图滚动到前一个单元格。 一旦我滚动到所需的单元格,我删除删除的单元格。

 - (void)removeScreen:(UIViewController *)screen animated:(BOOL)animated { NSParameterAssert(screen); NSInteger index = [[self.viewControllerDictionaries valueForKeyPath:kViewControllerKey] indexOfObject:screen]; if (index == NSNotFound) { return; } [screen willMoveToParentViewController:nil]; if (animated) { dispatch_time_t popTime = DISPATCH_TIME_NOW; NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:0]; // Disables user interaction to make sure the user can't interact with // the collection view during the time between when the scroll animation // ends and the deleted cell is removed. [self.collectionView setUserInteractionEnabled:NO]; // Scrolls to the previous item, if one exists. If we are at the first // item, we just let the next screen slide in from the right. if (index > 0) { popTime = dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC); NSIndexPath *targetIndexPath = [NSIndexPath indexPathForItem:index - 1 inSection:0]; [self.collectionView scrollToItemAtIndexPath:targetIndexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES]; } // Uses dispatch_after since -scrollToItemAtIndexPath:atScrollPosition:animated: // doesn't have a completion block. dispatch_after(popTime, dispatch_get_main_queue(), ^{ [self.collectionView performBatchUpdates:^{ [self.viewControllerDictionaries removeObjectAtIndex:index]; [self.collectionView deleteItemsAtIndexPaths:@[indexPath]]; [screen removeFromParentViewController]; [self.collectionView setUserInteractionEnabled:YES]; } completion:NULL]; }); } else { [self.viewControllerDictionaries removeObjectAtIndex:index]; [self.collectionView reloadData]; [screen removeFromParentViewController]; } self.addPageButton.enabled = YES; [self postScreenChangeNotification]; } 

稍有问题的部分是dispatch_after() 。 不幸的是, -scrollToItemAtIndexPath:atScrollPosition:animated:没有完成块,所以我不得不模拟它。 为了避免计时问题,我禁用了用户交互。 这可以防止用户在删除单元格之前与集合视图进行交互。

另一件事,我必须注意的是,我必须重置我的单元格的阿尔法回到1由于细胞重用。

我希望这可以帮助您使用Safari风格的选项卡select器。 我知道你的实现与我的不同,我希望我的解决scheme也适合你。

我知道这已经有了一个答案,但我用稍微不同的方式实现了这一点,不需要在设定的时间间隔后分派。

在你的删除方法,你会做一个检查,以确定是否最后一个项目被删除。 如果是这样的话:

 if(self.selection == self.assets.count-1 && self.selection != 0){ isDeleting = YES; [collection scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:self.selection-1 inSection:0] atScrollPosition:UICollectionViewScrollPositionLeft animated:YES]; } 

假设select是您正在删除的选定项目。 这将滚动到左侧的项目。 注意if语句检查这不是唯一的项目。 如果这是通话将崩溃,因为没有-1行。

然后你可以实现在滚动animation完成时调用的以下方法。 我只是在deleteObjectInCollection方法中将isDeleting设置为no,并且这一切似乎工作。

 - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView{ if(isDeleting){ [self deleteObjectInCollection]; } } 

我希望这有帮助。

还有另一个解决scheme(targetIndexPath是要删除的单元格的indexPath)。 这个代码可以简单地放在removeCellAtIndexPath:(NSIndexPath *)targetIndexPath方法中,并完成(假设我从我的代码到公共代码的改编是正确的,否则问我,我会试图帮助)。

 // (Here it's assumed that self.collectionView.collectionViewLayout has been assigned a UICollectionViewFlowLayout before; note the word "FLOW" in that class name) UICollectionViewFlowLayout* layout = (UICollectionViewFlowLayout*) self.collectionView.collectionViewLayout; // Find index path of the last cell in collection view: NSIndexPath* lastIndexPath = [self lastItemIndexPath]; // Extend content area temporarily to fill out space left by the last row after deletion, if last row is visible: BOOL lastRowWasVisible = NO; if ([self.collectionView icn_cellAtIndexPathIsVisible:lastIndexPath]) { lastRowWasVisible = YES; // Adapt section spacing to temporarily fill out the space potentially left after removing last cell: CGFloat cellWithLineSpacingHeight = 79.0f + 8.0f; // Height of my cell + one line spacing layout.sectionInset = UIEdgeInsetsMake(0.0f, 0.0f, cellWithLineSpacingHeight, 0.0f); } // Remove the cell: [self.collectionView performBatchUpdates:^{ [self.collectionView deleteItemsAtIndexPaths:[NSArray arrayWithObject:targetIndexPath]]; } completion:^(BOOL finished) { // Only scroll if we had the last row visible: if (lastRowWasVisible) { NSIndexPath* lastItemIndexPath = [self lastItemIndexPath]; // Run a custom scroll animation for two reasons; 1. that way we can reset the insets when animation is finished, and 2. using the "animated:YES" option lags here for some reason: [UIView animateWithDuration:0.3f animations:^{ [self.collectionView scrollToItemAtIndexPath:prevItemIndexPath atScrollPosition:UICollectionViewScrollPositionBottom animated:NO]; } completion:^(BOOL finished) { // Reset the space placeholder once having scrolled away from it: layout.sectionInset = UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 0.0f); }]; } }]; 

icn_cellAtIndexPathIsVisible:方法只是UICollectionView中的一个类别:

 - (BOOL) icn_cellAtIndexPathIsVisible:(NSIndexPath*)indexPath { BOOL __block wasVisible = NO; [self.indexPathsForVisibleItems enumerateObjectsUsingBlock:^(NSIndexPath* ip, NSUInteger idx, BOOL *stop) { if ([ip isEqual:indexPath]) { wasVisible = YES; *stop = YES; } }]; return wasVisible; } 

更新:这只适用于一个部分。

一个便宜的解决scheme是添加另一个0像素的单元格作为最后一个单元格,而不要删除它

 func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { if indexPath.row < collectibles.count - 1 { // dont delete the last one. just because the animation isn't working collectibles.removeAtIndex(indexPath.row) collectionView.deleteItemsAtIndexPaths([indexPath]) } } 

我正在面对一个类似的问题,通过图像集合视图单行水平滚动。 当我删除我的代码设置集合视图的contentSize它似乎自动处理这个问题消失了。

不是一个特别详细的答案,但我希望它有帮助。