diff --git a/Source/IGListCollectionViewLayout.h b/Source/IGListCollectionViewLayout.h index 558003a8..906e5641 100644 --- a/Source/IGListCollectionViewLayout.h +++ b/Source/IGListCollectionViewLayout.h @@ -88,6 +88,17 @@ NS_SWIFT_NAME(ListCollectionViewLayout) */ @property (nonatomic, assign) CGFloat stickyHeaderYOffset; +/** + Notify the layout that a specific section was modified before invalidation. Used to optimize layout re-calculation. + + @note When updating a collection view (ex: calling `-insertSections`), `-invalidateLayoutWithContext` gets called on + the layout object. However, the invalidation context doesn't provide details on which index paths are being modified, + which typically forces a full layout re-calculation. We can use this method to keep track of which section actually + needs to be updated on the following `-invalidateLayoutWithContext`. See `IGListCollectionView`. + + @param modifiedSection The section that was modified. + */ +- (void)didModifySection:(NSInteger)modifiedSection; /** Create and return a new collection view layout. diff --git a/Source/IGListCollectionViewLayout.mm b/Source/IGListCollectionViewLayout.mm index 324635b2..38f67e5e 100644 --- a/Source/IGListCollectionViewLayout.mm +++ b/Source/IGListCollectionViewLayout.mm @@ -67,6 +67,18 @@ static NSIndexPath *indexPathForSection(NSInteger section) { return [NSIndexPath indexPathForItem:0 inSection:section]; } +static NSInteger IGListMergeMinimumInvalidatedSection(NSInteger section, NSInteger otherSection) { + if (section == NSNotFound && otherSection == NSNotFound) { + return NSNotFound; + } else if (section == NSNotFound) { + return otherSection; + } else if (otherSection == NSNotFound) { + return section; + } + + return MIN(section, otherSection); +} + struct IGListSectionEntry { /** Represents the minimum-bounding box of every element in the section. This includes all item frames as well as the @@ -346,9 +358,12 @@ static void adjustZIndexForAttributes(UICollectionViewLayoutAttributes *attribut if (hasInvalidatedItemIndexPaths || [context invalidateEverything] - || [context invalidateDataSourceCounts] || context.ig_invalidateAllAttributes) { - _minimumInvalidatedSection = 0; // invalidates all + // invalidates all + _minimumInvalidatedSection = 0; + } else if ([context invalidateDataSourceCounts] && _minimumInvalidatedSection == NSNotFound) { + // invalidate all if count changed and we don't have information on the minimum invalidated section + _minimumInvalidatedSection = 0; } if (context.ig_invalidateSupplementaryAttributes) { @@ -610,4 +625,10 @@ static void adjustZIndexForAttributes(UICollectionViewLayoutAttributes *attribut }]; } +#pragma mark - Minimum Invalidated Section + +- (void)didModifySection:(NSInteger)modifiedSection { + _minimumInvalidatedSection = IGListMergeMinimumInvalidatedSection(_minimumInvalidatedSection, modifiedSection); +} + @end