I'm writing a custom UICollectionViewFlowLayout
and I've noticed that initialLayoutAttributesForAppearingItemAtIndexPath:
and initialLayoutAttributesForAppearingDecorationElementOfKind:atIndexPath:
will be called for all sections when I invoke performBatchUpdates:completion:
on the collection view. The result is that all sections have the animation applied to them instead of just the newly added section.
[collectionView performBatchUpdates:^{
currentModelArrayIndex++;
[collectionView insertSections:[NSIndexSet indexSetWithIndex:currentModelArrayIndex]];
[collectionView reloadSections:[NSIndexSet indexSetWithIndex:currentModelArrayIndex-1]];
} completion:^(BOOL finished) {
[collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:currentModelArrayIndex] atScrollPosition:UICollectionViewScrollPositionTop animated:YES];
}];
What I've tried so far is removing the call to performBatchUpdates:completion:
in lieu of simple updates, but the already existing sections (all of them) are animated in anyway. I've come up with a solution of checking to make sure that I'm changing the layout attributes of only the last section, but it feels hacky.
if (decorationIndexPath.section == [(id<UICollectionViewDataSource>)self.collectionView.delegate numberOfSectionsInCollectionView:self.collectionView] - 1)
{
layoutAttributes.alpha = 0.0f;
layoutAttributes.transform3D = CATransform3DMakeTranslation(-CGRectGetWidth(layoutAttributes.frame), 0, 0);
}
Is this the proper way to go about animating only some sections?
OK, I've got an answer; it's not a lot prettier than the previous one, but it keeps the layout from touching the datasource, so it's cleaner.
Basically, we need to override prepareForCollectionViewUpdates:
and finalizeCollectionViewUpdates
to keep track of the sections that we're inserting. I have a mutable set that contains NSNumber
instances of the sections that we're inserting.
-(void)prepareForCollectionViewUpdates:(NSArray *)updateItems
{
[super prepareForCollectionViewUpdates:updateItems];
[updateItems enumerateObjectsUsingBlock:^(UICollectionViewUpdateItem *updateItem, NSUInteger idx, BOOL *stop) {
if (updateItem.updateAction == UICollectionUpdateActionInsert)
{
[insertedSectionSet addObject:@(updateItem.indexPathAfterUpdate.section)];
}
}];
}
-(void)finalizeCollectionViewUpdates
{
[super finalizeCollectionViewUpdates];
[insertedSectionSet removeAllObjects];
}
Next, I check to see if the index path's section is included in that set when setting the initial layout attributes for items and decoration views.
-(UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingDecorationElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)decorationIndexPath
{
UICollectionViewLayoutAttributes *layoutAttributes;
if ([elementKind isEqualToString:AFCollectionViewFlowLayoutBackgroundDecoration])
{
if ([insertedSectionSet containsObject:@(decorationIndexPath.section)])
{
layoutAttributes = [self layoutAttributesForDecorationViewOfKind:elementKind atIndexPath:decorationIndexPath];
layoutAttributes.alpha = 0.0f;
layoutAttributes.transform3D = CATransform3DMakeTranslation(-CGRectGetWidth(layoutAttributes.frame), 0, 0);
}
}
return layoutAttributes;
}
-(UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
{
UICollectionViewLayoutAttributes *layoutAttributes;
if ([insertedSectionSet containsObject:@(itemIndexPath.section)])
{
layoutAttributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath];
layoutAttributes.transform3D = CATransform3DMakeTranslation([self collectionViewContentSize].width, 0, 0);
}
return layoutAttributes;
}
I'm returning nil
from these methods otherwise because nil
is the default value.
This has the added benefit of having nicer rotation animations, as well.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With