diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ec7a4000..8a07d9188 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ - Negate iOS 11 automatic estimated table row heights. [Christian Selig](https://github.com/christianselig) [#485](https://github.com/TextureGroup/Texture/pull/485) - [Breaking] Add content offset bridging property to ASTableNode and ASCollectionNode. Deprecate related methods in ASTableView and ASCollectionView [Huy Nguyen](https://github.com/nguyenhuy) [#460](https://github.com/TextureGroup/Texture/pull/460) - Remove re-entrant access to self.view when applying initial pending state. [Adlai Holler](https://github.com/Adlai-Holler) [#510](https://github.com/TextureGroup/Texture/pull/510) -- Small improvements in ASCollectionLayout [Huy Nguyen](https://github.com/nguyenhuy) [#509](https://github.com/TextureGroup/Texture/pull/509) +- Small improvements in ASCollectionLayout [Huy Nguyen](https://github.com/nguyenhuy) [#509](https://github.com/TextureGroup/Texture/pull/509) [#513](https://github.com/TextureGroup/Texture/pull/513) ##2.4 - Fix an issue where inserting/deleting sections could lead to inconsistent supplementary element behavior. [Adlai Holler](https://github.com/Adlai-Holler) diff --git a/Source/Details/ASCollectionLayoutState.mm b/Source/Details/ASCollectionLayoutState.mm index eb59edf0c..83226a143 100644 --- a/Source/Details/ASCollectionLayoutState.mm +++ b/Source/Details/ASCollectionLayoutState.mm @@ -170,9 +170,10 @@ - (UICollectionViewLayoutAttributes *)layoutAttributesForElement:(ASCollectionEl } - (ASPageToLayoutAttributesTable *)getAndRemoveUnmeasuredLayoutAttributesPageTableInRect:(CGRect)rect - contentSize:(CGSize)contentSize - pageSize:(CGSize)pageSize { + CGSize pageSize = _context.viewportSize; + CGSize contentSize = _contentSize; + ASDN::MutexLocker l(__instanceLock__); if (_unmeasuredPageToLayoutAttributesTable.count == 0 || CGRectIsNull(rect) || CGRectIsEmpty(rect) || CGSizeEqualToSize(CGSizeZero, contentSize) || CGSizeEqualToSize(CGSizeZero, pageSize)) { return nil; diff --git a/Source/Private/ASCollectionLayout.mm b/Source/Private/ASCollectionLayout.mm index 5f65f066d..97451c32a 100644 --- a/Source/Private/ASCollectionLayout.mm +++ b/Source/Private/ASCollectionLayout.mm @@ -93,7 +93,7 @@ + (ASCollectionLayoutState *)calculateLayoutWithContext:(ASCollectionLayoutConte ASCollectionLayoutState *layout = [context.layoutDelegateClass calculateLayoutWithContext:context]; [context.layoutCache setLayout:layout forContext:context]; - // Measure elements in the measure range ahead of time, block on the initial rect as it'll be visible shortly + // Measure elements in the measure range ahead of time CGSize viewportSize = context.viewportSize; CGPoint contentOffset = context.initialContentOffset; CGRect initialRect = CGRectMake(contentOffset.x, contentOffset.y, viewportSize.width, viewportSize.height); @@ -101,7 +101,11 @@ + (ASCollectionLayoutState *)calculateLayoutWithContext:(ASCollectionLayoutConte kASDefaultMeasureRangeTuningParameters, context.scrollableDirections, kASStaticScrollDirection); - [self _measureElementsInRect:measureRect blockingRect:initialRect layout:layout]; + // The first call to -layoutAttributesForElementsInRect: will be with a rect that is way bigger than initialRect here. + // If we only block on initialRect, a few elements that are outside of initialRect but inside measureRect + // may not be available by the time -layoutAttributesForElementsInRect: is called. + // Since this method is usually run off main, let's spawn more threads to measure and block on all elements in measureRect. + [self _measureElementsInRect:measureRect blockingRect:measureRect layout:layout]; return layout; } @@ -248,11 +252,7 @@ + (void)_measureElementsInRect:(CGRect)rect blockingRect:(CGRect)blockingRect la } // Step 2: Get layout attributes of all elements within the specified outer rect - ASCollectionLayoutContext *context = layout.context; - CGSize pageSize = context.viewportSize; - ASPageToLayoutAttributesTable *attrsTable = [layout getAndRemoveUnmeasuredLayoutAttributesPageTableInRect:rect - contentSize:contentSize - pageSize:pageSize]; + ASPageToLayoutAttributesTable *attrsTable = [layout getAndRemoveUnmeasuredLayoutAttributesPageTableInRect:rect]; if (attrsTable.count == 0) { // No elements in this rect! Bail early return; @@ -260,6 +260,8 @@ + (void)_measureElementsInRect:(CGRect)rect blockingRect:(CGRect)blockingRect la // Step 3: Split all those attributes into blocking and non-blocking buckets // Use ordered sets here because some items may span multiple pages, and the sets will be accessed by indexes later on. + ASCollectionLayoutContext *context = layout.context; + CGSize pageSize = context.viewportSize; NSMutableOrderedSet *blockingAttrs = hasBlockingRect ? [NSMutableOrderedSet orderedSet] : nil; NSMutableOrderedSet *nonBlockingAttrs = [NSMutableOrderedSet orderedSet]; for (id pagePtr in attrsTable) { diff --git a/Source/Private/ASCollectionLayoutState+Private.h b/Source/Private/ASCollectionLayoutState+Private.h index 170e57acc..97304b4af 100644 --- a/Source/Private/ASCollectionLayoutState+Private.h +++ b/Source/Private/ASCollectionLayoutState+Private.h @@ -22,9 +22,7 @@ NS_ASSUME_NONNULL_BEGIN * * @discussion This method is atomic and thread-safe */ -- (nullable ASPageToLayoutAttributesTable *)getAndRemoveUnmeasuredLayoutAttributesPageTableInRect:(CGRect)rect - contentSize:(CGSize)contentSize - pageSize:(CGSize)pageSize; +- (nullable ASPageToLayoutAttributesTable *)getAndRemoveUnmeasuredLayoutAttributesPageTableInRect:(CGRect)rect; @end