mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-04 21:41:45 +00:00
ASCollectionView: Suspend updates while reload data is pending
This commit is contained in:
parent
4e86ca2024
commit
77490e11c0
@ -131,6 +131,15 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
|
||||
@property (atomic, assign) BOOL asyncDataSourceLocked;
|
||||
|
||||
/**
|
||||
If YES, the `UICollectionView` will reload its data on next `layoutSubviews` so we should
|
||||
not forward any updates to it.
|
||||
|
||||
Rationale:
|
||||
In `reloadData`, a collection view invalidates its data and marks itself as needing reload, and waits until `layoutSubviews` to requery its data source. This can lead to data inconsistency problems. Say you have an empty collection view. You call `reloadData`, then immediately insert an item into your data source and call `insertItemsAtIndexPaths:[0,0]`. You will get an assertion failure saying "Invalid number of items in section 0. The number of items after the update (1) must be equal to the number of items before the update (1) plus or minus the items added and removed (1 added, 0 removed)." The collection view never queried your data source before the update to see that it actually had 0 items.
|
||||
*/
|
||||
@property (nonatomic) BOOL pendingReloadData;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ASCollectionView
|
||||
@ -207,6 +216,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
ASDisplayNodeAssert(self.asyncDelegate, @"ASCollectionView's asyncDelegate property must be set.");
|
||||
ASDisplayNodePerformBlockOnMainThread(^{
|
||||
[super reloadData];
|
||||
self.pendingReloadData = YES;
|
||||
});
|
||||
[_dataController reloadDataWithAnimationOptions:kASCollectionViewAnimationNone completion:completion];
|
||||
}
|
||||
@ -403,6 +413,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
|
||||
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
|
||||
{
|
||||
self.pendingReloadData = NO;
|
||||
return [_dataController numberOfSections];
|
||||
}
|
||||
|
||||
@ -485,7 +496,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
if (! CGSizeEqualToSize(_maxSizeForNodesConstrainedSize, self.bounds.size)) {
|
||||
if (!self.pendingReloadData && ! CGSizeEqualToSize(_maxSizeForNodesConstrainedSize, self.bounds.size)) {
|
||||
_maxSizeForNodesConstrainedSize = self.bounds.size;
|
||||
|
||||
// First size change occurs during initial configuration. An expensive relayout pass is unnecessary at that time
|
||||
@ -638,7 +649,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
- (void)rangeController:(ASRangeController *)rangeController endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion {
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
if (!self.asyncDataSource) {
|
||||
if (!self.asyncDataSource || self.pendingReloadData) {
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
@ -690,7 +701,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
if (!self.asyncDataSource) {
|
||||
if (!self.asyncDataSource || self.pendingReloadData) {
|
||||
return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes
|
||||
}
|
||||
|
||||
@ -709,7 +720,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
if (!self.asyncDataSource) {
|
||||
if (!self.asyncDataSource || self.pendingReloadData) {
|
||||
return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes
|
||||
}
|
||||
|
||||
@ -728,7 +739,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
if (!self.asyncDataSource) {
|
||||
if (!self.asyncDataSource || self.pendingReloadData) {
|
||||
return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes
|
||||
}
|
||||
|
||||
@ -747,7 +758,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
if (!self.asyncDataSource) {
|
||||
if (!self.asyncDataSource || self.pendingReloadData) {
|
||||
return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user