Don't Cache ConstrainedSize in ASCollectionView (#3136)

* Never reuse constrained sizes when measuring collection view items

* Assert main thread

* Fix the build
This commit is contained in:
Adlai Holler 2017-03-06 10:11:56 -08:00 committed by GitHub
parent d59ea3902d
commit 1d6221c52b

View File

@ -33,6 +33,7 @@
#import <AsyncDisplayKit/ASCollectionView+Undeprecated.h>
#import <AsyncDisplayKit/_ASHierarchyChangeSet.h>
#import <AsyncDisplayKit/CoreGraphics+ASConvenience.h>
#import <AsyncDisplayKit/ASLayout.h>
/**
* A macro to get self.collectionNode and assign it to a local variable, or return
@ -606,9 +607,28 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
return _zeroContentInsets;
}
/// Uses latest size range from data source and -layoutThatFits:.
- (CGSize)sizeForElement:(ASCollectionElement *)element
{
ASDisplayNodeAssertMainThread();
NSString *supplementaryKind = element.supplementaryElementKind;
NSIndexPath *indexPath = [_dataController.visibleMap indexPathForElement:element];
ASSizeRange sizeRange;
if (supplementaryKind == nil) {
sizeRange = [self dataController:_dataController constrainedSizeForNodeAtIndexPath:indexPath];
} else {
sizeRange = [self dataController:_dataController constrainedSizeForSupplementaryNodeOfKind:supplementaryKind atIndexPath:indexPath];
}
return [element.node layoutThatFits:sizeRange].size;
}
- (CGSize)calculatedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath
{
return [[self nodeForItemAtIndexPath:indexPath] calculatedSize];
ASDisplayNodeAssertMainThread();
ASCollectionElement *e = [_dataController.visibleMap elementForItemAtIndexPath:indexPath];
return [self sizeForElement:e];
}
- (ASCellNode *)nodeForItemAtIndexPath:(NSIndexPath *)indexPath
@ -893,37 +913,45 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
ASDisplayNodeAssertMainThread();
ASCellNode *cell = [self nodeForItemAtIndexPath:indexPath];
if (cell.shouldUseUIKitCell) {
if ([_asyncDelegate respondsToSelector:@selector(collectionView:layout:sizeForItemAtIndexPath:)]) {
return [(id)_asyncDelegate collectionView:collectionView layout:collectionViewLayout sizeForItemAtIndexPath:indexPath];
}
}
return cell.calculatedSize;
ASCollectionElement *e = [_dataController.visibleMap elementForItemAtIndexPath:indexPath];
return [self sizeForElement:e];
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout referenceSizeForHeaderInSection:(NSInteger)section
{
ASDisplayNodeAssertMainThread();
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:section];
ASCellNode *cell = [self supplementaryNodeForElementKind:UICollectionElementKindSectionHeader
atIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]];
atIndexPath:indexPath];
if (cell.shouldUseUIKitCell && _asyncDelegateFlags.interop) {
if ([_asyncDelegate respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]) {
return [(id)_asyncDelegate collectionView:collectionView layout:layout referenceSizeForHeaderInSection:section];
}
}
return cell.calculatedSize;
ASCollectionElement *e = [_dataController.visibleMap supplementaryElementOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath];
return [self sizeForElement:e];
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout referenceSizeForFooterInSection:(NSInteger)section
{
ASDisplayNodeAssertMainThread();
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:section];
ASCellNode *cell = [self supplementaryNodeForElementKind:UICollectionElementKindSectionFooter
atIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]];
atIndexPath:indexPath];
if (cell.shouldUseUIKitCell && _asyncDelegateFlags.interop) {
if ([_asyncDelegate respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]) {
return [(id)_asyncDelegate collectionView:collectionView layout:layout referenceSizeForFooterInSection:section];
}
}
return cell.calculatedSize;
ASCollectionElement *e = [_dataController.visibleMap supplementaryElementOfKind:UICollectionElementKindSectionFooter atIndexPath:indexPath];
return [self sizeForElement:e];
}
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
@ -1959,6 +1987,15 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
#pragma mark ASCALayerExtendedDelegate
/**
* TODO: This code was added when we used @c calculatedSize as the size for
* items (e.g. collectionView:layout:sizeForItemAtIndexPath:) and so it
* was critical that we remeasured all nodes at this time.
*
* The assumption was that cv-bounds-size-change -> constrained-size-change, so
* this was the time when we get new constrained sizes for all items and remeasure
* them. However, the constrained sizes for items can be invalidated for many other
* reasons, hence why we never reuse the old constrained size anymore.
*
* UICollectionView inadvertently triggers a -prepareLayout call to its layout object
* between [super setFrame:] and [self layoutSubviews] during size changes. So we need
* to get in there and re-measure our nodes before that -prepareLayout call.