Skip to content
This repository has been archived by the owner on Feb 2, 2023. It is now read-only.

[ASCollectionView] Add Method to Wait for Updates During Next Layout Pass #2186

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions AsyncDisplayKit/ASCollectionView.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, weak) id<ASCollectionDelegate> asyncDelegate;
@property (nonatomic, weak) id<ASCollectionDataSource> asyncDataSource;

/**
* Tell the collection view that, during the next layoutSubviews pass, it should block the
* main thread and wait for all items to be up-to-date.
*
* @discussion This is useful when you want to ensure that the user never sees an empty
* collection view. It is better to call this than to explicitly wait using `waitUntilAllUpdatesAreCommitted`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a @warning in the waitUntilAllUpdatesAreCommitted method indicating they might want to use this method instead?

* because the collection view's size may not be correct until the layout pass.
*/
- (void)waitForUpdatesDuringNextLayoutPass;

/**
* Tuning parameters for a range type in full mode.
*
Expand Down
36 changes: 32 additions & 4 deletions AsyncDisplayKit/ASCollectionView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ @interface ASCollectionView () <ASRangeControllerDataSource, ASRangeControllerDe
NSMutableSet *_cellsForVisibilityUpdates;
id<ASCollectionViewLayoutFacilitatorProtocol> _layoutFacilitator;

BOOL _isInSuperLayoutSubviews;
BOOL _waitsForUpdatesDuringNextLayout;
BOOL _performingBatchUpdates;
NSUInteger _superBatchUpdateCount;
NSMutableArray *_batchUpdateBlocks;
Expand Down Expand Up @@ -283,21 +285,27 @@ - (void)dealloc

- (void)reloadDataWithCompletion:(void (^)())completion
{
ASPerformBlockOnMainThread(^{
_superIsPendingDataLoad = YES;
[super reloadData];
});
ASDisplayNodeAssertMainThread();

_superIsPendingDataLoad = YES;
[_dataController reloadDataWithAnimationOptions:kASCollectionViewAnimationNone completion:completion];
if (_isInSuperLayoutSubviews && _waitsForUpdatesDuringNextLayout) {
_waitsForUpdatesDuringNextLayout = NO;
[self waitUntilAllUpdatesAreCommitted];
}
[super reloadData];
}

- (void)reloadData
{
ASDisplayNodeAssertMainThread();
[self reloadDataWithCompletion:nil];
}

- (void)reloadDataImmediately
{
ASDisplayNodeAssertMainThread();

_superIsPendingDataLoad = YES;
[_dataController reloadDataImmediatelyWithAnimationOptions:kASCollectionViewAnimationNone];
[super reloadData];
Expand All @@ -311,6 +319,11 @@ - (void)relayoutItems
- (void)waitUntilAllUpdatesAreCommitted
{
ASDisplayNodeAssertMainThread();

if (_dataController.initialReloadDataHasBeenCalled == NO) {
[_dataController reloadDataWithAnimationOptions:kASCollectionViewAnimationNone completion:nil];
}

[_dataController waitUntilAllUpdatesAreCommitted];
}

Expand Down Expand Up @@ -520,6 +533,12 @@ - (void)selectItemAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated s
[super selectItemAtIndexPath:indexPath animated:animated scrollPosition:scrollPosition];
}

- (void)waitForUpdatesDuringNextLayoutPass
{
ASDisplayNodeAssertMainThread();
_waitsForUpdatesDuringNextLayout = YES;
}

#pragma mark Internal

/**
Expand Down Expand Up @@ -870,8 +889,17 @@ - (void)layoutSubviews
break;
}

// If we've already done our initial reload, we can just wait now.
// Otherwise we will wait after our initial reloadData (during [super layoutSubviews])
if (_dataController.initialReloadDataHasBeenCalled && _waitsForUpdatesDuringNextLayout) {
_waitsForUpdatesDuringNextLayout = NO;
[self waitUntilAllUpdatesAreCommitted];
}

// To ensure _maxSizeForNodesConstrainedSize is up-to-date for every usage, this call to super must be done last
_isInSuperLayoutSubviews = YES;
[super layoutSubviews];
_isInSuperLayoutSubviews = NO;

// Update range controller immediately if possible & needed.
// Calling -updateIfNeeded in here with self.window == nil (early in the collection view's life)
Expand Down
12 changes: 11 additions & 1 deletion AsyncDisplayKit/ASTableView.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,16 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style;

/**
* Tell the table view that, during the next layoutSubviews pass, it should block the
* main thread and wait for all rows to be up-to-date.
*
* @discussion This is useful when you want to ensure that the user never sees an empty
* table view. It is better to call this than to explicitly wait using `waitUntilAllUpdatesAreCommitted`
* because the table view's size may not be correct until the layout pass.
*/
- (void)waitForUpdatesDuringNextLayoutPass;

/**
* Tuning parameters for a range type in full mode.
*
Expand Down Expand Up @@ -162,7 +172,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^ _Nullable)(BOOL completed))completion;

/**
* Blocks execution of the main thread until all section and row updates are committed. This method must be called from the main thread.
* Blocks execution of the main thread until all section and row updates are committed. This method must be called from the main thread.
*/
- (void)waitUntilAllUpdatesAreCommitted;

Expand Down
37 changes: 32 additions & 5 deletions AsyncDisplayKit/ASTableView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ @interface ASTableView () <ASRangeControllerDataSource, ASRangeControllerDelegat
*/
CALayer *_retainedLayer;

BOOL _waitsForUpdatesDuringNextLayout;
BOOL _isInSuperLayoutSubviews;
CGFloat _nodesConstrainedWidth;
BOOL _ignoreNodesConstrainedWidthChange;
BOOL _queuedNodeHeightUpdate;
Expand Down Expand Up @@ -269,8 +271,7 @@ - (void)dealloc
[self setAsyncDataSource:nil];
}

#pragma mark -
#pragma mark Overrides
#pragma mark - Overrides

- (void)setDataSource:(id<UITableViewDataSource>)dataSource
{
Expand All @@ -284,6 +285,8 @@ - (void)setDelegate:(id<UITableViewDelegate>)delegate
ASDisplayNodeAssert(delegate == nil, @"ASTableView uses asyncDelegate, not UITableView's delegate property.");
}

#pragma mark - Public

- (void)setAsyncDataSource:(id<ASTableViewDataSource>)asyncDataSource
{
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
Expand Down Expand Up @@ -361,10 +364,14 @@ - (void)proxyTargetHasDeallocated:(ASDelegateProxy *)proxy

- (void)reloadDataWithCompletion:(void (^)())completion
{
ASPerformBlockOnMainThread(^{
[super reloadData];
});
ASDisplayNodeAssertMainThread();

[_dataController reloadDataWithAnimationOptions:UITableViewRowAnimationNone completion:completion];
if (_isInSuperLayoutSubviews && _waitsForUpdatesDuringNextLayout) {
_waitsForUpdatesDuringNextLayout = NO;
[self waitUntilAllUpdatesAreCommitted];
}
[super reloadData];
}

- (void)reloadData
Expand All @@ -384,6 +391,12 @@ - (void)relayoutItems
[_dataController relayoutAllNodes];
}

- (void)waitForUpdatesDuringNextLayoutPass
{
ASDisplayNodeAssertMainThread();
_waitsForUpdatesDuringNextLayout = YES;
}

- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType
{
[_rangeController setTuningParameters:tuningParameters forRangeMode:ASLayoutRangeModeFull rangeType:rangeType];
Expand Down Expand Up @@ -455,6 +468,11 @@ - (void)endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL completed))c
- (void)waitUntilAllUpdatesAreCommitted
{
ASDisplayNodeAssertMainThread();

if (_dataController.initialReloadDataHasBeenCalled == NO) {
[_dataController reloadDataWithAnimationOptions:UITableViewRowAnimationNone completion:nil];
}

[_dataController waitUntilAllUpdatesAreCommitted];
}

Expand Down Expand Up @@ -498,8 +516,17 @@ - (void)layoutSubviews
}
}

// If we've already done our initial reload, we can just wait now.
// Otherwise we will wait after our initial reloadData (during [super layoutSubviews])
if (_dataController.initialReloadDataHasBeenCalled && _waitsForUpdatesDuringNextLayout) {
_waitsForUpdatesDuringNextLayout = NO;
[self waitUntilAllUpdatesAreCommitted];
}

// To ensure _nodesConstrainedWidth is up-to-date for every usage, this call to super must be done last
_isInSuperLayoutSubviews = YES;
[super layoutSubviews];
_isInSuperLayoutSubviews = NO;
[_rangeController updateIfNeeded];
}

Expand Down
Loading