当从 UICollectionView 中插入或删除项目时,在动画过程中似乎出现了一个额外的单元格,并且这个额外的单元格向错误的方向移动。我已经用 UITableView 进行了完全相同的尝试,没有问题。
问题视频在这里:https://dl.dropbox.com/u/11523469/CollectionViewBug.mov ,左边是 Collection View ,右边是 TableView 。每个单元格中的数字是创建单元格时单元格的indexPath.item值。
问题首先在视频的 0:08 到 0:12(插入)之间出现,然后在 0:16 到 0:20(删除)再次出现。
项目在这里可用:https://dl.dropbox.com/u/11523469/CollectionViewBug.zip
即插入单元格时,插入单元格下方的所有单元格都会向下移动以为新单元格腾出空间。但是这个额外的单元格出现并与其他单元格重叠并向上移动。
同样,当删除一个单元格时,被删除单元格下方的所有单元格都会向上移动以填充该单元格原来所在的空隙。但是这个额外的单元格出现并与其他单元格重叠并向下移动。
对 Collection View 执行的第一个操作,无论是插入还是删除,都不会导致这个问题。但在所有后续操作中,问题都存在。
有没有其他人遇到过与 UICollectionView 相同的问题?有人有解决方案或解决方法吗?
谢谢!
我想出了一个解决方法,它似乎可以解决问题,但对所提供的示例非常具体。我的猜测是,当单元格被重用时,它们的起点错误,这会导致出现奇怪的动画。
我更改了 Storyboard以使用 UICollectionViewFlowLayout 的子类:
// MyFlowLayout - subclass of UICollectionViewFlowLayout
#import "MyFlowLayout.h"
@interface MyFlowLayout ()
@property (strong) NSMutableArray *deleteIndexPaths;
@property (strong) NSMutableArray *insertIndexPaths;
@property (assign) float rowOffset;
@end
@implementation MyFlowLayout
-(id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder])
{
// minimumInteritemSpacing may be adjusted upwards but this example ignores that
self.rowOffset = self.itemSize.height + self.minimumInteritemSpacing;
}
return self;
}
// As per Mark Pospesel corrections to CircleLayout
- (void)prepareForCollectionViewUpdates:(NSArray *)updateItems
{
// Keep track of insert and delete index paths
[super prepareForCollectionViewUpdates:updateItems];
self.deleteIndexPaths = [NSMutableArray array];
self.insertIndexPaths = [NSMutableArray array];
for (UICollectionViewUpdateItem *update in updateItems)
{
if (update.updateAction == UICollectionUpdateActionDelete)
{
[self.deleteIndexPaths addObject:update.indexPathBeforeUpdate];
}
else if (update.updateAction == UICollectionUpdateActionInsert)
{
[self.insertIndexPaths addObject:update.indexPathAfterUpdate];
}
}
}
- (void)finalizeCollectionViewUpdates
{
[super finalizeCollectionViewUpdates];
// release the insert and delete index paths
self.deleteIndexPaths = nil;
self.insertIndexPaths = nil;
}
// The next two methods have misleading names as they get called for all visible cells on both insert and delete
- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
{
// Must call super
UICollectionViewLayoutAttributes *attributes = [super initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath];
if (!attributes)
attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath];
if ([self.insertIndexPaths containsObject:itemIndexPath]) {
// Initial position for an inserted cell is it's final position - fades in
CGRect frame = attributes.frame;
frame.origin.y = itemIndexPath.row * self.rowOffset;
attributes.frame = frame;
attributes.zIndex = -1; // stop the inserted cell bleeding through too early in the animation
}
if ([self.deleteIndexPaths count]) {
NSIndexPath *deletedPath = self.deleteIndexPaths[0]; // Might be more than one but this example ignores that
if (itemIndexPath.row > deletedPath.row) {
// Anything after the deleted cell needs to slide up from the position below it's final position
// Anything before the deleted cell doesn't need adjusting
CGRect frame = attributes.frame;
frame.origin.y = ((itemIndexPath.row + 1) * self.rowOffset);
attributes.frame = frame;
}
}
return attributes;
}
- (UICollectionViewLayoutAttributes *)finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
{
UICollectionViewLayoutAttributes *attributes = [super finalLayoutAttributesForDisappearingItemAtIndexPath:itemIndexPath];
if (!attributes)
attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath];
// I would have expected the final positions to already be correct but my guess is that re-used cells
// are not considered until after the animation block settings have been generated
CGRect frame = attributes.frame;
frame.origin.y = itemIndexPath.row * self.rowOffset;
attributes.frame = frame;
if ([self.deleteIndexPaths containsObject:itemIndexPath]) {
// Fade out the deleted cell
attributes.alpha = 0.0;
}
return attributes;
}
@end