mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-17 11:50:56 +00:00
Fix double-load issue with ASCollectionNode (#372)
* Fix double-reload issue with ASCollectionNode * Add a message to the change pile * Fix some license headers * Address feedback
This commit is contained in:
parent
4829a8643d
commit
326925839d
@ -9,6 +9,7 @@
|
|||||||
- [Yoga] Implement ASYogaLayoutSpec, a simplified integration strategy for Yoga-powered layout calculation. [Scott Goodson](https://github.com/appleguy)
|
- [Yoga] Implement ASYogaLayoutSpec, a simplified integration strategy for Yoga-powered layout calculation. [Scott Goodson](https://github.com/appleguy)
|
||||||
- Fixed an issue where calls to setNeedsDisplay and setNeedsLayout would stop working on loaded nodes. [Garrett Moon](https://github.com/garrettmoon)
|
- Fixed an issue where calls to setNeedsDisplay and setNeedsLayout would stop working on loaded nodes. [Garrett Moon](https://github.com/garrettmoon)
|
||||||
- Migrated unit tests to OCMock 3.4 (from 2.2) and improved the multiplex image node tests. [Adlai Holler](https://github.com/Adlai-Holler)
|
- Migrated unit tests to OCMock 3.4 (from 2.2) and improved the multiplex image node tests. [Adlai Holler](https://github.com/Adlai-Holler)
|
||||||
|
- Fix CollectionNode double-load issue. This should significantly improve performance in cases where a collection node has content immediately available on first layout i.e. not fetched from the network. [Adlai Holler](https://github.com/Adlai-Holler)
|
||||||
|
|
||||||
## 2.3.3
|
## 2.3.3
|
||||||
- [ASTextKitFontSizeAdjuster] Replace use of NSAttributedString's boundingRectWithSize:options:context: with NSLayoutManager's boundingRectForGlyphRange:inTextContainer: [Ricky Cancro](https://github.com/rcancro)
|
- [ASTextKitFontSizeAdjuster] Replace use of NSAttributedString's boundingRectWithSize:options:context: with NSLayoutManager's boundingRectForGlyphRange:inTextContainer: [Ricky Cancro](https://github.com/rcancro)
|
||||||
|
|||||||
@ -28,6 +28,7 @@
|
|||||||
#import <AsyncDisplayKit/ASDisplayNode+FrameworkPrivate.h>
|
#import <AsyncDisplayKit/ASDisplayNode+FrameworkPrivate.h>
|
||||||
#import <AsyncDisplayKit/ASInternalHelpers.h>
|
#import <AsyncDisplayKit/ASInternalHelpers.h>
|
||||||
#import <AsyncDisplayKit/ASCellNode+Internal.h>
|
#import <AsyncDisplayKit/ASCellNode+Internal.h>
|
||||||
|
#import <AsyncDisplayKit/_ASHierarchyChangeSet.h>
|
||||||
#import <AsyncDisplayKit/AsyncDisplayKit+Debug.h>
|
#import <AsyncDisplayKit/AsyncDisplayKit+Debug.h>
|
||||||
#import <AsyncDisplayKit/ASSectionContext.h>
|
#import <AsyncDisplayKit/ASSectionContext.h>
|
||||||
#import <AsyncDisplayKit/ASDataController.h>
|
#import <AsyncDisplayKit/ASDataController.h>
|
||||||
@ -682,9 +683,17 @@
|
|||||||
- (void)reloadDataWithCompletion:(void (^)())completion
|
- (void)reloadDataWithCompletion:(void (^)())completion
|
||||||
{
|
{
|
||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
if (self.nodeLoaded) {
|
if (!self.nodeLoaded) {
|
||||||
[self.view reloadDataWithCompletion:completion];
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[self performBatchUpdates:^{
|
||||||
|
[self.view.changeSet reloadData];
|
||||||
|
} completion:^(BOOL finished){
|
||||||
|
if (completion) {
|
||||||
|
completion();
|
||||||
|
}
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)reloadData
|
- (void)reloadData
|
||||||
@ -692,14 +701,19 @@
|
|||||||
[self reloadDataWithCompletion:nil];
|
[self reloadDataWithCompletion:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)relayoutItems
|
|
||||||
{
|
|
||||||
[self.view relayoutItems];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)reloadDataImmediately
|
- (void)reloadDataImmediately
|
||||||
{
|
{
|
||||||
[self.view reloadDataImmediately];
|
ASDisplayNodeAssertMainThread();
|
||||||
|
[self reloadData];
|
||||||
|
[self waitUntilAllUpdatesAreCommitted];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)relayoutItems
|
||||||
|
{
|
||||||
|
ASDisplayNodeAssertMainThread();
|
||||||
|
if (self.nodeLoaded) {
|
||||||
|
[self.view relayoutItems];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)beginUpdates
|
- (void)beginUpdates
|
||||||
|
|||||||
@ -266,14 +266,14 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
* the main thread.
|
* the main thread.
|
||||||
* @warning This method is substantially more expensive than UICollectionView's version.
|
* @warning This method is substantially more expensive than UICollectionView's version.
|
||||||
*/
|
*/
|
||||||
- (void)reloadDataWithCompletion:(nullable void (^)())completion ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead.");
|
- (void)reloadDataWithCompletion:(nullable void (^)())completion AS_UNAVAILABLE("Use ASCollectionNode method instead.");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reload everything from scratch, destroying the working range and all cached nodes.
|
* Reload everything from scratch, destroying the working range and all cached nodes.
|
||||||
*
|
*
|
||||||
* @warning This method is substantially more expensive than UICollectionView's version.
|
* @warning This method is substantially more expensive than UICollectionView's version.
|
||||||
*/
|
*/
|
||||||
- (void)reloadData ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead.");
|
- (void)reloadData AS_UNAVAILABLE("Use ASCollectionNode method instead.");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reload everything from scratch entirely on the main thread, destroying the working range and all cached nodes.
|
* Reload everything from scratch entirely on the main thread, destroying the working range and all cached nodes.
|
||||||
@ -281,7 +281,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
* @warning This method is substantially more expensive than UICollectionView's version and will block the main thread
|
* @warning This method is substantially more expensive than UICollectionView's version and will block the main thread
|
||||||
* while all the cells load.
|
* while all the cells load.
|
||||||
*/
|
*/
|
||||||
- (void)reloadDataImmediately ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode's -reloadDataWithCompletion: followed by -waitUntilAllUpdatesAreCommitted instead.");
|
- (void)reloadDataImmediately AS_UNAVAILABLE("Use ASCollectionNode method instead.");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggers a relayout of all nodes.
|
* Triggers a relayout of all nodes.
|
||||||
|
|||||||
@ -144,11 +144,6 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
|||||||
* (0 sections) we always check at least once after each update (initial reload is the first update.)
|
* (0 sections) we always check at least once after each update (initial reload is the first update.)
|
||||||
*/
|
*/
|
||||||
BOOL _hasEverCheckedForBatchFetchingDueToUpdate;
|
BOOL _hasEverCheckedForBatchFetchingDueToUpdate;
|
||||||
|
|
||||||
/**
|
|
||||||
* The change set that we're currently building, if any.
|
|
||||||
*/
|
|
||||||
_ASHierarchyChangeSet *_changeSet;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Counter used to keep track of nested batch updates.
|
* Counter used to keep track of nested batch updates.
|
||||||
@ -333,31 +328,23 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
|||||||
#pragma mark -
|
#pragma mark -
|
||||||
#pragma mark Overrides.
|
#pragma mark Overrides.
|
||||||
|
|
||||||
- (void)reloadDataWithCompletion:(void (^)())completion
|
/**
|
||||||
{
|
* This method is not available to be called by the public i.e.
|
||||||
ASDisplayNodeAssertMainThread();
|
* it should only be called by UICollectionView itself. UICollectionView
|
||||||
|
* does this e.g. during the first layout pass, or if you call -numberOfSections
|
||||||
if (! _dataController.initialReloadDataHasBeenCalled) {
|
* before its content is loaded.
|
||||||
// If this is the first reload, forward to super immediately to prevent it from triggering more "initial" loads while our data controller is working.
|
*/
|
||||||
_superIsPendingDataLoad = YES;
|
|
||||||
[super reloadData];
|
|
||||||
}
|
|
||||||
|
|
||||||
void (^batchUpdatesCompletion)(BOOL);
|
|
||||||
if (completion) {
|
|
||||||
batchUpdatesCompletion = ^(BOOL) {
|
|
||||||
completion();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[self performBatchUpdates:^{
|
|
||||||
[_changeSet reloadData];
|
|
||||||
} completion:batchUpdatesCompletion];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)reloadData
|
- (void)reloadData
|
||||||
{
|
{
|
||||||
[self reloadDataWithCompletion:nil];
|
[super reloadData];
|
||||||
|
|
||||||
|
// UICollectionView calls -reloadData during first layoutSubviews and when the data source changes.
|
||||||
|
// This fires off the first load of cell nodes.
|
||||||
|
if (_asyncDataSource != nil && !self.dataController.initialReloadDataHasBeenCalled) {
|
||||||
|
[self performBatchUpdates:^{
|
||||||
|
[_changeSet reloadData];
|
||||||
|
} completion:nil];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animated
|
- (void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animated
|
||||||
@ -367,13 +354,6 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)reloadDataImmediately
|
|
||||||
{
|
|
||||||
ASDisplayNodeAssertMainThread();
|
|
||||||
[self reloadData];
|
|
||||||
[self waitUntilAllUpdatesAreCommitted];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)relayoutItems
|
- (void)relayoutItems
|
||||||
{
|
{
|
||||||
[_dataController relayoutAllNodes];
|
[_dataController relayoutAllNodes];
|
||||||
@ -1721,7 +1701,6 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
|||||||
}
|
}
|
||||||
UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath];
|
UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath];
|
||||||
return CGSizeEqualToSizeWithIn(attributes.size, size, FLT_EPSILON);
|
return CGSizeEqualToSizeWithIn(attributes.size, size, FLT_EPSILON);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - ASDataControllerSource optional methods
|
#pragma mark - ASDataControllerSource optional methods
|
||||||
|
|||||||
@ -31,6 +31,11 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
@property (nonatomic, strong, readonly) ASDataController *dataController;
|
@property (nonatomic, strong, readonly) ASDataController *dataController;
|
||||||
@property (nonatomic, strong, readonly) ASRangeController *rangeController;
|
@property (nonatomic, strong, readonly) ASRangeController *rangeController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The change set that we're currently building, if any.
|
||||||
|
*/
|
||||||
|
@property (nonatomic, strong, nullable, readonly) _ASHierarchyChangeSet *changeSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see ASCollectionNode+Beta.h for full documentation.
|
* @see ASCollectionNode+Beta.h for full documentation.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -163,30 +163,6 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
*/
|
*/
|
||||||
- (void)performBatchUpdates:(nullable AS_NOESCAPE void (^)())updates completion:(nullable void (^)(BOOL finished))completion;
|
- (void)performBatchUpdates:(nullable AS_NOESCAPE void (^)())updates completion:(nullable void (^)(BOOL finished))completion;
|
||||||
|
|
||||||
/**
|
|
||||||
* Reload everything from scratch, destroying the working range and all cached nodes.
|
|
||||||
*
|
|
||||||
* @param completion block to run on completion of asynchronous loading or nil. If supplied, the block is run on
|
|
||||||
* the main thread.
|
|
||||||
* @warning This method is substantially more expensive than UICollectionView's version.
|
|
||||||
*/
|
|
||||||
- (void)reloadDataWithCompletion:(nullable void (^)())completion;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reload everything from scratch, destroying the working range and all cached nodes.
|
|
||||||
*
|
|
||||||
* @warning This method is substantially more expensive than UICollectionView's version.
|
|
||||||
*/
|
|
||||||
- (void)reloadData;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reload everything from scratch entirely on the main thread, destroying the working range and all cached nodes.
|
|
||||||
*
|
|
||||||
* @warning This method is substantially more expensive than UICollectionView's version and will block the main thread
|
|
||||||
* while all the cells load.
|
|
||||||
*/
|
|
||||||
- (void)reloadDataImmediately;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggers a relayout of all nodes.
|
* Triggers a relayout of all nodes.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -151,26 +151,30 @@
|
|||||||
|
|
||||||
- (void)loadInitialData
|
- (void)loadInitialData
|
||||||
{
|
{
|
||||||
/// BUG: these methods are called twice in a row i.e. this for-loop shouldn't be here. https://github.com/TextureGroup/Texture/issues/351
|
// Count methods are called twice in a row for first data load.
|
||||||
|
// Since -reloadData is routed through our batch update system,
|
||||||
|
// the batch update latches the "old data source counts" if needed at -beginUpdates time
|
||||||
|
// and then verifies them against the "new data source counts" after the updates.
|
||||||
|
// This isn't ideal, but the cost is very small and the system works well.
|
||||||
for (int i = 0; i < 2; i++) {
|
for (int i = 0; i < 2; i++) {
|
||||||
// It reads all the counts
|
// It reads all the counts
|
||||||
[self expectDataSourceCountMethods];
|
[self expectDataSourceCountMethods];
|
||||||
|
}
|
||||||
|
|
||||||
// It reads each section object.
|
// It reads each section object.
|
||||||
for (NSInteger section = 0; section < sections.count; section++) {
|
for (NSInteger section = 0; section < sections.count; section++) {
|
||||||
[self expectContextMethodForSection:section];
|
[self expectContextMethodForSection:section];
|
||||||
}
|
}
|
||||||
|
|
||||||
// It reads the contents for each item.
|
// It reads the contents for each item.
|
||||||
for (NSInteger section = 0; section < sections.count; section++) {
|
for (NSInteger section = 0; section < sections.count; section++) {
|
||||||
NSArray *viewModels = sections[section].viewModels;
|
NSArray *viewModels = sections[section].viewModels;
|
||||||
|
|
||||||
// For each item:
|
// For each item:
|
||||||
for (NSInteger i = 0; i < viewModels.count; i++) {
|
for (NSInteger i = 0; i < viewModels.count; i++) {
|
||||||
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:section];
|
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:section];
|
||||||
[self expectViewModelMethodForItemAtIndexPath:indexPath viewModel:viewModels[i]];
|
[self expectViewModelMethodForItemAtIndexPath:indexPath viewModel:viewModels[i]];
|
||||||
[self expectNodeBlockMethodForItemAtIndexPath:indexPath];
|
[self expectNodeBlockMethodForItemAtIndexPath:indexPath];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// ASCollectionViewTests.m
|
// ASCollectionViewTests.mm
|
||||||
// Texture
|
// Texture
|
||||||
//
|
//
|
||||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||||
@ -259,7 +259,8 @@
|
|||||||
[window setRootViewController:testController];
|
[window setRootViewController:testController];
|
||||||
[window makeKeyAndVisible];
|
[window makeKeyAndVisible];
|
||||||
|
|
||||||
[testController.collectionView reloadDataImmediately];
|
[testController.collectionNode reloadData];
|
||||||
|
[testController.collectionNode waitUntilAllUpdatesAreCommitted];
|
||||||
[testController.collectionView layoutIfNeeded];
|
[testController.collectionView layoutIfNeeded];
|
||||||
|
|
||||||
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
|
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
|
||||||
@ -390,12 +391,13 @@
|
|||||||
ASCollectionViewTestController *testController = [[ASCollectionViewTestController alloc] initWithNibName:nil bundle:nil];\
|
ASCollectionViewTestController *testController = [[ASCollectionViewTestController alloc] initWithNibName:nil bundle:nil];\
|
||||||
__unused ASCollectionViewTestDelegate *del = testController.asyncDelegate;\
|
__unused ASCollectionViewTestDelegate *del = testController.asyncDelegate;\
|
||||||
__unused ASCollectionView *cv = testController.collectionView;\
|
__unused ASCollectionView *cv = testController.collectionView;\
|
||||||
__unused ASCollectionNode *cn = testController.collectionNode;\
|
ASCollectionNode *cn = testController.collectionNode;\
|
||||||
UIWindow *window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];\
|
UIWindow *window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];\
|
||||||
[window makeKeyAndVisible]; \
|
[window makeKeyAndVisible]; \
|
||||||
window.rootViewController = testController;\
|
window.rootViewController = testController;\
|
||||||
\
|
\
|
||||||
[testController.collectionView reloadDataImmediately];\
|
[cn reloadData];\
|
||||||
|
[cn waitUntilAllUpdatesAreCommitted]; \
|
||||||
[testController.collectionView layoutIfNeeded];
|
[testController.collectionView layoutIfNeeded];
|
||||||
|
|
||||||
- (void)testThatSubmittingAValidInsertDoesNotThrowAnException
|
- (void)testThatSubmittingAValidInsertDoesNotThrowAnException
|
||||||
@ -620,7 +622,7 @@
|
|||||||
for (NSInteger i = 0; i < 2; i++) {
|
for (NSInteger i = 0; i < 2; i++) {
|
||||||
// NOTE: waitUntilAllUpdatesAreCommitted or reloadDataImmediately is not sufficient here!!
|
// NOTE: waitUntilAllUpdatesAreCommitted or reloadDataImmediately is not sufficient here!!
|
||||||
XCTestExpectation *done = [self expectationWithDescription:[NSString stringWithFormat:@"Reload #%td complete", i]];
|
XCTestExpectation *done = [self expectationWithDescription:[NSString stringWithFormat:@"Reload #%td complete", i]];
|
||||||
[cv reloadDataWithCompletion:^{
|
[cn reloadDataWithCompletion:^{
|
||||||
[done fulfill];
|
[done fulfill];
|
||||||
}];
|
}];
|
||||||
[self waitForExpectationsWithTimeout:1 handler:nil];
|
[self waitForExpectationsWithTimeout:1 handler:nil];
|
||||||
@ -752,7 +754,8 @@
|
|||||||
updateValidationTestPrologue
|
updateValidationTestPrologue
|
||||||
|
|
||||||
del.sectionGeneration++;
|
del.sectionGeneration++;
|
||||||
[cv reloadDataImmediately];
|
[cn reloadData];
|
||||||
|
[cn waitUntilAllUpdatesAreCommitted];
|
||||||
|
|
||||||
NSInteger sectionCount = del->_itemCounts.size();
|
NSInteger sectionCount = del->_itemCounts.size();
|
||||||
for (NSInteger section = 0; section < sectionCount; section++) {
|
for (NSInteger section = 0; section < sectionCount; section++) {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// ASDisplayNodeLayoutTests.m
|
// ASDisplayNodeLayoutTests.mm
|
||||||
// Texture
|
// Texture
|
||||||
//
|
//
|
||||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user