mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 14:45:21 +00:00
[ASRangeController] Update synchronously when possible
This commit is contained in:
committed by
Adlai Holler
parent
2e3da9bc92
commit
edb4e45c24
@@ -272,6 +272,10 @@
|
|||||||
{
|
{
|
||||||
[super visibleStateDidChange:isVisible];
|
[super visibleStateDidChange:isVisible];
|
||||||
|
|
||||||
|
if (isVisible && self.neverShowPlaceholders) {
|
||||||
|
[self recursivelyEnsureDisplaySynchronously:YES];
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: This assertion is failing in some apps and will be enabled soon.
|
// NOTE: This assertion is failing in some apps and will be enabled soon.
|
||||||
// ASDisplayNodeAssert(self.isNodeLoaded, @"Node should be loaded in order for it to become visible or invisible. If not in this situation, we shouldn't trigger creating the view.");
|
// ASDisplayNodeAssert(self.isNodeLoaded, @"Node should be loaded in order for it to become visible or invisible. If not in this situation, we shouldn't trigger creating the view.");
|
||||||
UIView *view = self.view;
|
UIView *view = self.view;
|
||||||
|
|||||||
@@ -639,11 +639,8 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
[_asyncDelegate collectionView:self willDisplayNodeForItemAtIndexPath:indexPath];
|
[_asyncDelegate collectionView:self willDisplayNodeForItemAtIndexPath:indexPath];
|
||||||
}
|
}
|
||||||
|
|
||||||
[_rangeController visibleNodeIndexPathsDidChangeWithScrollDirection:self.scrollDirection];
|
[_rangeController setNeedsUpdate];
|
||||||
|
|
||||||
if (cellNode.neverShowPlaceholders) {
|
|
||||||
[cellNode recursivelyEnsureDisplaySynchronously:YES];
|
|
||||||
}
|
|
||||||
if (ASSubclassOverridesSelector([ASCellNode class], [cellNode class], @selector(cellNodeVisibilityEvent:inScrollView:withCellFrame:))) {
|
if (ASSubclassOverridesSelector([ASCellNode class], [cellNode class], @selector(cellNodeVisibilityEvent:inScrollView:withCellFrame:))) {
|
||||||
[_cellsForVisibilityUpdates addObject:cell];
|
[_cellsForVisibilityUpdates addObject:cell];
|
||||||
}
|
}
|
||||||
@@ -651,8 +648,6 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
|
|
||||||
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(_ASCollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
|
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(_ASCollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
|
||||||
{
|
{
|
||||||
[_rangeController visibleNodeIndexPathsDidChangeWithScrollDirection:self.scrollDirection];
|
|
||||||
|
|
||||||
ASCellNode *cellNode = [cell node];
|
ASCellNode *cellNode = [cell node];
|
||||||
|
|
||||||
if (_asyncDelegateFlags.asyncDelegateCollectionViewDidEndDisplayingNodeForItemAtIndexPath) {
|
if (_asyncDelegateFlags.asyncDelegateCollectionViewDidEndDisplayingNodeForItemAtIndexPath) {
|
||||||
@@ -660,9 +655,9 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
[_asyncDelegate collectionView:self didEndDisplayingNode:cellNode forItemAtIndexPath:indexPath];
|
[_asyncDelegate collectionView:self didEndDisplayingNode:cellNode forItemAtIndexPath:indexPath];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([_cellsForVisibilityUpdates containsObject:cell]) {
|
[_rangeController setNeedsUpdate];
|
||||||
|
|
||||||
[_cellsForVisibilityUpdates removeObject:cell];
|
[_cellsForVisibilityUpdates removeObject:cell];
|
||||||
}
|
|
||||||
|
|
||||||
#pragma clang diagnostic push
|
#pragma clang diagnostic push
|
||||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||||
@@ -844,6 +839,13 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
|
|
||||||
// To ensure _maxSizeForNodesConstrainedSize is up-to-date for every usage, this call to super must be done last
|
// To ensure _maxSizeForNodesConstrainedSize is up-to-date for every usage, this call to super must be done last
|
||||||
[super layoutSubviews];
|
[super layoutSubviews];
|
||||||
|
|
||||||
|
// Update range controller immediately if possible & needed.
|
||||||
|
// Calling -updateIfNeeded in here with self.window == nil (early in the collection view's life)
|
||||||
|
// may cause UICollectionView data related crashes. We'll update in -didMoveToWindow anyway.
|
||||||
|
if (self.window != nil) {
|
||||||
|
[_rangeController updateIfNeeded];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1030,13 +1032,17 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
- (NSArray *)visibleNodeIndexPathsForRangeController:(ASRangeController *)rangeController
|
- (NSArray *)visibleNodeIndexPathsForRangeController:(ASRangeController *)rangeController
|
||||||
{
|
{
|
||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
|
// Calling -indexPathsForVisibleItems will trigger UIKit to call reloadData if it never has, which can result
|
||||||
// Calling visibleNodeIndexPathsForRangeController: will trigger UIKit to call reloadData if it never has, which can result
|
|
||||||
// in incorrect layout if performed at zero size. We can use the fact that nothing can be visible at zero size to return fast.
|
// in incorrect layout if performed at zero size. We can use the fact that nothing can be visible at zero size to return fast.
|
||||||
BOOL isZeroSized = CGRectEqualToRect(self.bounds, CGRectZero);
|
BOOL isZeroSized = CGRectEqualToRect(self.bounds, CGRectZero);
|
||||||
return isZeroSized ? @[] : [self indexPathsForVisibleItems];
|
return isZeroSized ? @[] : [self indexPathsForVisibleItems];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (ASScrollDirection)scrollDirectionForRangeController:(ASRangeController *)rangeController
|
||||||
|
{
|
||||||
|
return self.scrollDirection;
|
||||||
|
}
|
||||||
|
|
||||||
- (CGSize)viewportSizeForRangeController:(ASRangeController *)rangeController
|
- (CGSize)viewportSizeForRangeController:(ASRangeController *)rangeController
|
||||||
{
|
{
|
||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
@@ -1085,9 +1091,13 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
block();
|
block();
|
||||||
}
|
}
|
||||||
} completion:^(BOOL finished){
|
} completion:^(BOOL finished){
|
||||||
|
// Flush any range changes that happened as part of the update animations ending.
|
||||||
|
[_rangeController updateIfNeeded];
|
||||||
[self _scheduleCheckForBatchFetchingForNumberOfChanges:numberOfUpdateBlocks];
|
[self _scheduleCheckForBatchFetchingForNumberOfChanges:numberOfUpdateBlocks];
|
||||||
if (completion) { completion(finished); }
|
if (completion) { completion(finished); }
|
||||||
}];
|
}];
|
||||||
|
// Flush any range changes that happened as part of submitting the update.
|
||||||
|
[_rangeController updateIfNeeded];
|
||||||
});
|
});
|
||||||
|
|
||||||
[_batchUpdateBlocks removeAllObjects];
|
[_batchUpdateBlocks removeAllObjects];
|
||||||
@@ -1114,6 +1124,8 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
} else {
|
} else {
|
||||||
[UIView performWithoutAnimation:^{
|
[UIView performWithoutAnimation:^{
|
||||||
[super insertItemsAtIndexPaths:indexPaths];
|
[super insertItemsAtIndexPaths:indexPaths];
|
||||||
|
// Flush any range changes that happened as part of submitting the update.
|
||||||
|
[_rangeController updateIfNeeded];
|
||||||
[self _scheduleCheckForBatchFetchingForNumberOfChanges:indexPaths.count];
|
[self _scheduleCheckForBatchFetchingForNumberOfChanges:indexPaths.count];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -1134,6 +1146,8 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
} else {
|
} else {
|
||||||
[UIView performWithoutAnimation:^{
|
[UIView performWithoutAnimation:^{
|
||||||
[super deleteItemsAtIndexPaths:indexPaths];
|
[super deleteItemsAtIndexPaths:indexPaths];
|
||||||
|
// Flush any range changes that happened as part of submitting the update.
|
||||||
|
[_rangeController updateIfNeeded];
|
||||||
[self _scheduleCheckForBatchFetchingForNumberOfChanges:indexPaths.count];
|
[self _scheduleCheckForBatchFetchingForNumberOfChanges:indexPaths.count];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -1154,6 +1168,8 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
} else {
|
} else {
|
||||||
[UIView performWithoutAnimation:^{
|
[UIView performWithoutAnimation:^{
|
||||||
[super insertSections:indexSet];
|
[super insertSections:indexSet];
|
||||||
|
// Flush any range changes that happened as part of submitting the update.
|
||||||
|
[_rangeController updateIfNeeded];
|
||||||
[self _scheduleCheckForBatchFetchingForNumberOfChanges:indexSet.count];
|
[self _scheduleCheckForBatchFetchingForNumberOfChanges:indexSet.count];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -1174,6 +1190,8 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
} else {
|
} else {
|
||||||
[UIView performWithoutAnimation:^{
|
[UIView performWithoutAnimation:^{
|
||||||
[super deleteSections:indexSet];
|
[super deleteSections:indexSet];
|
||||||
|
// Flush any range changes that happened as part of submitting the update.
|
||||||
|
[_rangeController updateIfNeeded];
|
||||||
[self _scheduleCheckForBatchFetchingForNumberOfChanges:indexSet.count];
|
[self _scheduleCheckForBatchFetchingForNumberOfChanges:indexSet.count];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -1275,7 +1293,8 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
// Updating the visible node index paths only for not range managed nodes. Range managed nodes will get their
|
// Updating the visible node index paths only for not range managed nodes. Range managed nodes will get their
|
||||||
// their update in the layout pass
|
// their update in the layout pass
|
||||||
if (![node supportsRangeManagedInterfaceState]) {
|
if (![node supportsRangeManagedInterfaceState]) {
|
||||||
[_rangeController visibleNodeIndexPathsDidChangeWithScrollDirection:self.scrollDirection];
|
[_rangeController setNeedsUpdate];
|
||||||
|
[_rangeController updateIfNeeded];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#import "ASLayout.h"
|
#import "ASLayout.h"
|
||||||
#import "_ASDisplayLayer.h"
|
#import "_ASDisplayLayer.h"
|
||||||
#import "ASTableNode.h"
|
#import "ASTableNode.h"
|
||||||
|
#import "ASEqualityHelpers.h"
|
||||||
|
|
||||||
static const ASSizeRange kInvalidSizeRange = {CGSizeZero, CGSizeZero};
|
static const ASSizeRange kInvalidSizeRange = {CGSizeZero, CGSizeZero};
|
||||||
static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||||
@@ -126,6 +127,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
BOOL _ignoreNodesConstrainedWidthChange;
|
BOOL _ignoreNodesConstrainedWidthChange;
|
||||||
BOOL _queuedNodeHeightUpdate;
|
BOOL _queuedNodeHeightUpdate;
|
||||||
BOOL _isDeallocating;
|
BOOL _isDeallocating;
|
||||||
|
BOOL _performingBatchUpdates;
|
||||||
NSMutableSet *_cellsForVisibilityUpdates;
|
NSMutableSet *_cellsForVisibilityUpdates;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@@ -468,6 +470,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
|
|
||||||
// To ensure _nodesConstrainedWidth is up-to-date for every usage, this call to super must be done last
|
// To ensure _nodesConstrainedWidth is up-to-date for every usage, this call to super must be done last
|
||||||
[super layoutSubviews];
|
[super layoutSubviews];
|
||||||
|
[_rangeController updateIfNeeded];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark -
|
#pragma mark -
|
||||||
@@ -644,35 +647,29 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
[_asyncDelegate tableView:self willDisplayNodeForRowAtIndexPath:indexPath];
|
[_asyncDelegate tableView:self willDisplayNodeForRowAtIndexPath:indexPath];
|
||||||
}
|
}
|
||||||
|
|
||||||
[_rangeController visibleNodeIndexPathsDidChangeWithScrollDirection:[self scrollDirection]];
|
[_rangeController setNeedsUpdate];
|
||||||
|
|
||||||
if (cellNode.neverShowPlaceholders) {
|
|
||||||
[cellNode recursivelyEnsureDisplaySynchronously:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ASSubclassOverridesSelector([ASCellNode class], [cellNode class], @selector(cellNodeVisibilityEvent:inScrollView:withCellFrame:))) {
|
if (ASSubclassOverridesSelector([ASCellNode class], [cellNode class], @selector(cellNodeVisibilityEvent:inScrollView:withCellFrame:))) {
|
||||||
[_cellsForVisibilityUpdates addObject:cell];
|
[_cellsForVisibilityUpdates addObject:cell];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(_ASTableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath
|
- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(_ASTableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
|
||||||
{
|
{
|
||||||
if ([_pendingVisibleIndexPath isEqual:indexPath]) {
|
if (ASObjectIsEqual(_pendingVisibleIndexPath, indexPath)) {
|
||||||
_pendingVisibleIndexPath = nil;
|
_pendingVisibleIndexPath = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASCellNode *cellNode = [cell node];
|
ASCellNode *cellNode = [cell node];
|
||||||
|
|
||||||
[_rangeController visibleNodeIndexPathsDidChangeWithScrollDirection:[self scrollDirection]];
|
[_rangeController setNeedsUpdate];
|
||||||
|
|
||||||
if (_asyncDelegateFlags.asyncDelegateTableViewDidEndDisplayingNodeForRowAtIndexPath) {
|
if (_asyncDelegateFlags.asyncDelegateTableViewDidEndDisplayingNodeForRowAtIndexPath) {
|
||||||
ASDisplayNodeAssertNotNil(cellNode, @"Expected node associated with removed cell not to be nil.");
|
ASDisplayNodeAssertNotNil(cellNode, @"Expected node associated with removed cell not to be nil.");
|
||||||
[_asyncDelegate tableView:self didEndDisplayingNode:cellNode forRowAtIndexPath:indexPath];
|
[_asyncDelegate tableView:self didEndDisplayingNode:cellNode forRowAtIndexPath:indexPath];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([_cellsForVisibilityUpdates containsObject:cell]) {
|
|
||||||
[_cellsForVisibilityUpdates removeObject:cell];
|
[_cellsForVisibilityUpdates removeObject:cell];
|
||||||
}
|
|
||||||
|
|
||||||
#pragma clang diagnostic push
|
#pragma clang diagnostic push
|
||||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||||
@@ -866,61 +863,38 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
return @[];
|
return @[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// In this case we cannot use indexPathsForVisibleRows in this case to get all the visible index paths as apparently
|
// NOTE: A prior comment claimed that `indexPathsForVisibleRows` may return extra index paths for grouped-style
|
||||||
// in a grouped UITableView it would return index paths for cells that are over the edge of the visible area.
|
// tables. This is seen as an acceptable issue for the time being.
|
||||||
// Unfortunatly this means we never get a call for -tableView:cellForRowAtIndexPath: for that cells, but we will mark
|
|
||||||
// mark them as visible in the range controller
|
NSIndexPath *pendingVisibleIndexPath = _pendingVisibleIndexPath;
|
||||||
NSMutableArray *visibleIndexPaths = [NSMutableArray array];
|
if (pendingVisibleIndexPath == nil) {
|
||||||
for (id cell in self.visibleCells) {
|
return self.indexPathsForVisibleRows;
|
||||||
[visibleIndexPaths addObject:[self indexPathForCell:cell]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pendingVisibleIndexPath) {
|
NSMutableArray *visibleIndexPaths = [self.indexPathsForVisibleRows mutableCopy];
|
||||||
NSMutableSet *indexPaths = [NSMutableSet setWithArray:visibleIndexPaths];
|
[visibleIndexPaths sortUsingSelector:@selector(compare:)];
|
||||||
|
|
||||||
BOOL (^isAfter)(NSIndexPath *, NSIndexPath *) = ^BOOL(NSIndexPath *indexPath, NSIndexPath *anchor) {
|
BOOL isPendingIndexPathVisible = (NSNotFound != [visibleIndexPaths indexOfObject:pendingVisibleIndexPath inSortedRange:NSMakeRange(0, visibleIndexPaths.count) options:kNilOptions usingComparator:^(id _Nonnull obj1, id _Nonnull obj2) {
|
||||||
if (!anchor || !indexPath) {
|
return [obj1 compare:obj2];
|
||||||
return NO;
|
}]);
|
||||||
}
|
|
||||||
if (indexPath.section == anchor.section) {
|
|
||||||
return (indexPath.row == anchor.row+1); // assumes that indexes are valid
|
|
||||||
|
|
||||||
} else if (indexPath.section > anchor.section && indexPath.row == 0) {
|
if (isPendingIndexPathVisible) {
|
||||||
if (anchor.row != [_dataController numberOfRowsInSection:anchor.section] -1) {
|
|
||||||
return NO; // anchor is not at the end of the section
|
|
||||||
}
|
|
||||||
|
|
||||||
NSInteger nextSection = anchor.section+1;
|
|
||||||
while([_dataController numberOfRowsInSection:nextSection] == 0) {
|
|
||||||
++nextSection;
|
|
||||||
}
|
|
||||||
|
|
||||||
return indexPath.section == nextSection;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NO;
|
|
||||||
};
|
|
||||||
|
|
||||||
BOOL (^isBefore)(NSIndexPath *, NSIndexPath *) = ^BOOL(NSIndexPath *indexPath, NSIndexPath *anchor) {
|
|
||||||
return isAfter(anchor, indexPath);
|
|
||||||
};
|
|
||||||
|
|
||||||
if ([indexPaths containsObject:_pendingVisibleIndexPath]) {
|
|
||||||
_pendingVisibleIndexPath = nil; // once it has shown up in visibleIndexPaths, we can stop tracking it
|
_pendingVisibleIndexPath = nil; // once it has shown up in visibleIndexPaths, we can stop tracking it
|
||||||
} else if (!isBefore(_pendingVisibleIndexPath, visibleIndexPaths.firstObject) &&
|
} else if ([self isIndexPath:visibleIndexPaths.firstObject immediateSuccessorOfIndexPath:pendingVisibleIndexPath]) {
|
||||||
!isAfter(_pendingVisibleIndexPath, visibleIndexPaths.lastObject)) {
|
[visibleIndexPaths insertObject:pendingVisibleIndexPath atIndex:0];
|
||||||
_pendingVisibleIndexPath = nil; // not contiguous, ignore.
|
} else if ([self isIndexPath:pendingVisibleIndexPath immediateSuccessorOfIndexPath:visibleIndexPaths.lastObject]) {
|
||||||
|
[visibleIndexPaths addObject:pendingVisibleIndexPath];
|
||||||
} else {
|
} else {
|
||||||
[indexPaths addObject:_pendingVisibleIndexPath];
|
_pendingVisibleIndexPath = nil; // not contiguous, ignore.
|
||||||
|
|
||||||
[visibleIndexPaths removeAllObjects];
|
|
||||||
[visibleIndexPaths addObjectsFromArray:[indexPaths.allObjects sortedArrayUsingSelector:@selector(compare:)]];
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return visibleIndexPaths;
|
return visibleIndexPaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (ASScrollDirection)scrollDirectionForRangeController:(ASRangeController *)rangeController
|
||||||
|
{
|
||||||
|
return self.scrollDirection;
|
||||||
|
}
|
||||||
|
|
||||||
- (NSArray *)rangeController:(ASRangeController *)rangeController nodesAtIndexPaths:(NSArray *)indexPaths
|
- (NSArray *)rangeController:(ASRangeController *)rangeController nodesAtIndexPaths:(NSArray *)indexPaths
|
||||||
{
|
{
|
||||||
return [_dataController nodesAtIndexPaths:indexPaths];
|
return [_dataController nodesAtIndexPaths:indexPaths];
|
||||||
@@ -953,6 +927,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes
|
return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_performingBatchUpdates = YES;
|
||||||
[super beginUpdates];
|
[super beginUpdates];
|
||||||
|
|
||||||
if (_automaticallyAdjustsContentOffset) {
|
if (_automaticallyAdjustsContentOffset) {
|
||||||
@@ -978,8 +953,10 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
|
|
||||||
ASPerformBlockWithoutAnimation(!animated, ^{
|
ASPerformBlockWithoutAnimation(!animated, ^{
|
||||||
[super endUpdates];
|
[super endUpdates];
|
||||||
|
[_rangeController updateIfNeeded];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_performingBatchUpdates = NO;
|
||||||
if (completion) {
|
if (completion) {
|
||||||
completion(YES);
|
completion(YES);
|
||||||
}
|
}
|
||||||
@@ -1005,6 +982,9 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
NSLog(@"-[super insertRowsAtIndexPaths]: %@", indexPaths);
|
NSLog(@"-[super insertRowsAtIndexPaths]: %@", indexPaths);
|
||||||
}
|
}
|
||||||
[super insertRowsAtIndexPaths:indexPaths withRowAnimation:(UITableViewRowAnimation)animationOptions];
|
[super insertRowsAtIndexPaths:indexPaths withRowAnimation:(UITableViewRowAnimation)animationOptions];
|
||||||
|
if (!_performingBatchUpdates) {
|
||||||
|
[_rangeController updateIfNeeded];
|
||||||
|
}
|
||||||
[self _scheduleCheckForBatchFetchingForNumberOfChanges:indexPaths.count];
|
[self _scheduleCheckForBatchFetchingForNumberOfChanges:indexPaths.count];
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1028,6 +1008,9 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
NSLog(@"-[super deleteRowsAtIndexPaths]: %@", indexPaths);
|
NSLog(@"-[super deleteRowsAtIndexPaths]: %@", indexPaths);
|
||||||
}
|
}
|
||||||
[super deleteRowsAtIndexPaths:indexPaths withRowAnimation:(UITableViewRowAnimation)animationOptions];
|
[super deleteRowsAtIndexPaths:indexPaths withRowAnimation:(UITableViewRowAnimation)animationOptions];
|
||||||
|
if (!_performingBatchUpdates) {
|
||||||
|
[_rangeController updateIfNeeded];
|
||||||
|
}
|
||||||
[self _scheduleCheckForBatchFetchingForNumberOfChanges:indexPaths.count];
|
[self _scheduleCheckForBatchFetchingForNumberOfChanges:indexPaths.count];
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1052,6 +1035,9 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
NSLog(@"-[super insertSections]: %@", indexSet);
|
NSLog(@"-[super insertSections]: %@", indexSet);
|
||||||
}
|
}
|
||||||
[super insertSections:indexSet withRowAnimation:(UITableViewRowAnimation)animationOptions];
|
[super insertSections:indexSet withRowAnimation:(UITableViewRowAnimation)animationOptions];
|
||||||
|
if (!_performingBatchUpdates) {
|
||||||
|
[_rangeController updateIfNeeded];
|
||||||
|
}
|
||||||
[self _scheduleCheckForBatchFetchingForNumberOfChanges:indexSet.count];
|
[self _scheduleCheckForBatchFetchingForNumberOfChanges:indexSet.count];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1071,6 +1057,9 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
NSLog(@"-[super deleteSections]: %@", indexSet);
|
NSLog(@"-[super deleteSections]: %@", indexSet);
|
||||||
}
|
}
|
||||||
[super deleteSections:indexSet withRowAnimation:(UITableViewRowAnimation)animationOptions];
|
[super deleteSections:indexSet withRowAnimation:(UITableViewRowAnimation)animationOptions];
|
||||||
|
if (!_performingBatchUpdates) {
|
||||||
|
[_rangeController updateIfNeeded];
|
||||||
|
}
|
||||||
[self _scheduleCheckForBatchFetchingForNumberOfChanges:indexSet.count];
|
[self _scheduleCheckForBatchFetchingForNumberOfChanges:indexSet.count];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1232,6 +1221,32 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
[_rangeController clearFetchedData];
|
[_rangeController clearFetchedData];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - Helper Methods
|
||||||
|
|
||||||
|
- (BOOL)isIndexPath:(NSIndexPath *)indexPath immediateSuccessorOfIndexPath:(NSIndexPath *)anchor
|
||||||
|
{
|
||||||
|
if (!anchor || !indexPath) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
if (indexPath.section == anchor.section) {
|
||||||
|
return (indexPath.row == anchor.row+1); // assumes that indexes are valid
|
||||||
|
|
||||||
|
} else if (indexPath.section > anchor.section && indexPath.row == 0) {
|
||||||
|
if (anchor.row != [_dataController numberOfRowsInSection:anchor.section] -1) {
|
||||||
|
return NO; // anchor is not at the end of the section
|
||||||
|
}
|
||||||
|
|
||||||
|
NSInteger nextSection = anchor.section+1;
|
||||||
|
while([_dataController numberOfRowsInSection:nextSection] == 0) {
|
||||||
|
++nextSection;
|
||||||
|
}
|
||||||
|
|
||||||
|
return indexPath.section == nextSection;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark - _ASDisplayView behavior substitutions
|
#pragma mark - _ASDisplayView behavior substitutions
|
||||||
// Need these to drive interfaceState so we know when we are visible, if not nested in another range-managing element.
|
// Need these to drive interfaceState so we know when we are visible, if not nested in another range-managing element.
|
||||||
// Because our superclass is a true UIKit class, we cannot also subclass _ASDisplayView.
|
// Because our superclass is a true UIKit class, we cannot also subclass _ASDisplayView.
|
||||||
@@ -1255,7 +1270,8 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
// Updating the visible node index paths only for not range managed nodes. Range managed nodes will get their
|
// Updating the visible node index paths only for not range managed nodes. Range managed nodes will get their
|
||||||
// their update in the layout pass
|
// their update in the layout pass
|
||||||
if (![node supportsRangeManagedInterfaceState]) {
|
if (![node supportsRangeManagedInterfaceState]) {
|
||||||
[_rangeController visibleNodeIndexPathsDidChangeWithScrollDirection:[self scrollDirection]];
|
[_rangeController setNeedsUpdate];
|
||||||
|
[_rangeController updateIfNeeded];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,12 +40,18 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
/**
|
/**
|
||||||
* Notify the range controller that the visible range has been updated.
|
* Notify the range controller that the visible range has been updated.
|
||||||
* This is the primary input call that drives updating the working ranges, and triggering their actions.
|
* This is the primary input call that drives updating the working ranges, and triggering their actions.
|
||||||
*
|
* The ranges will be updated in the next turn of the main loop, or when -updateIfNeeded is called.
|
||||||
* @param scrollDirection The current scroll direction of the scroll view.
|
|
||||||
*
|
*
|
||||||
* @see [ASRangeControllerDelegate rangeControllerVisibleNodeIndexPaths:]
|
* @see [ASRangeControllerDelegate rangeControllerVisibleNodeIndexPaths:]
|
||||||
*/
|
*/
|
||||||
- (void)visibleNodeIndexPathsDidChangeWithScrollDirection:(ASScrollDirection)scrollDirection;
|
- (void)setNeedsUpdate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the ranges immediately, if -setNeedsUpdate has been called since the last update.
|
||||||
|
* This is useful because the ranges must be updated immediately after a cell is added
|
||||||
|
* into a table/collection to satisfy interface state API guarantees.
|
||||||
|
*/
|
||||||
|
- (void)updateIfNeeded;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the sized node for `indexPath` as a subview of `contentView`.
|
* Add the sized node for `indexPath` as a subview of `contentView`.
|
||||||
@@ -101,6 +107,13 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
*/
|
*/
|
||||||
- (NSArray<NSIndexPath *> *)visibleNodeIndexPathsForRangeController:(ASRangeController *)rangeController;
|
- (NSArray<NSIndexPath *> *)visibleNodeIndexPathsForRangeController:(ASRangeController *)rangeController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param rangeController Sender.
|
||||||
|
*
|
||||||
|
* @returns the current scroll direction of the view using this range controller.
|
||||||
|
*/
|
||||||
|
- (ASScrollDirection)scrollDirectionForRangeController:(ASRangeController *)rangeController;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param rangeController Sender.
|
* @param rangeController Sender.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -19,17 +19,23 @@
|
|||||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||||
#import "ASCellNode.h"
|
#import "ASCellNode.h"
|
||||||
|
|
||||||
|
#define AS_RANGECONTROLLER_LOG_UPDATE_FREQ 0
|
||||||
|
|
||||||
@interface ASRangeController ()
|
@interface ASRangeController ()
|
||||||
{
|
{
|
||||||
BOOL _rangeIsValid;
|
BOOL _rangeIsValid;
|
||||||
BOOL _queuedRangeUpdate;
|
BOOL _needsRangeUpdate;
|
||||||
BOOL _layoutControllerImplementsSetVisibleIndexPaths;
|
BOOL _layoutControllerImplementsSetVisibleIndexPaths;
|
||||||
ASScrollDirection _scrollDirection;
|
|
||||||
NSSet<NSIndexPath *> *_allPreviousIndexPaths;
|
NSSet<NSIndexPath *> *_allPreviousIndexPaths;
|
||||||
ASLayoutRangeMode _currentRangeMode;
|
ASLayoutRangeMode _currentRangeMode;
|
||||||
BOOL _didUpdateCurrentRange;
|
BOOL _didUpdateCurrentRange;
|
||||||
BOOL _didRegisterForNodeDisplayNotifications;
|
BOOL _didRegisterForNodeDisplayNotifications;
|
||||||
CFAbsoluteTime _pendingDisplayNodesTimestamp;
|
CFAbsoluteTime _pendingDisplayNodesTimestamp;
|
||||||
|
|
||||||
|
#if AS_RANGECONTROLLER_LOG_UPDATE_FREQ
|
||||||
|
NSUInteger _updateCountThisFrame;
|
||||||
|
CADisplayLink *_displayLink;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -52,11 +58,20 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive;
|
|||||||
|
|
||||||
[[[self class] allRangeControllersWeakSet] addObject:self];
|
[[[self class] allRangeControllersWeakSet] addObject:self];
|
||||||
|
|
||||||
|
#if AS_RANGECONTROLLER_LOG_UPDATE_FREQ
|
||||||
|
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_updateCountDisplayLinkDidFire)];
|
||||||
|
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
|
||||||
|
#endif
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
|
#if AS_RANGECONTROLLER_LOG_UPDATE_FREQ
|
||||||
|
[_displayLink invalidate];
|
||||||
|
#endif
|
||||||
|
|
||||||
if (_didRegisterForNodeDisplayNotifications) {
|
if (_didRegisterForNodeDisplayNotifications) {
|
||||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:ASRenderingEngineDidDisplayScheduledNodesNotification object:nil];
|
[[NSNotificationCenter defaultCenter] removeObserver:self name:ASRenderingEngineDidDisplayScheduledNodesNotification object:nil];
|
||||||
}
|
}
|
||||||
@@ -94,12 +109,25 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive;
|
|||||||
return selfInterfaceState;
|
return selfInterfaceState;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)visibleNodeIndexPathsDidChangeWithScrollDirection:(ASScrollDirection)scrollDirection
|
- (void)setNeedsUpdate
|
||||||
{
|
{
|
||||||
_scrollDirection = scrollDirection;
|
if (!_needsRangeUpdate) {
|
||||||
|
_needsRangeUpdate = YES;
|
||||||
|
|
||||||
// Perform update immediately, so that cells receive a visibleStateDidChange: call before their first pixel is visible.
|
__weak __typeof__(self) weakSelf = self;
|
||||||
[self scheduleRangeUpdate];
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[weakSelf updateIfNeeded];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)updateIfNeeded
|
||||||
|
{
|
||||||
|
if (_needsRangeUpdate) {
|
||||||
|
_needsRangeUpdate = NO;
|
||||||
|
|
||||||
|
[self _updateVisibleNodeIndexPaths];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updateCurrentRangeWithMode:(ASLayoutRangeMode)rangeMode
|
- (void)updateCurrentRangeWithMode:(ASLayoutRangeMode)rangeMode
|
||||||
@@ -108,69 +136,51 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive;
|
|||||||
_currentRangeMode = rangeMode;
|
_currentRangeMode = rangeMode;
|
||||||
_didUpdateCurrentRange = YES;
|
_didUpdateCurrentRange = YES;
|
||||||
|
|
||||||
[self scheduleRangeUpdate];
|
[self setNeedsUpdate];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)scheduleRangeUpdate
|
|
||||||
{
|
|
||||||
if (_queuedRangeUpdate) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// coalesce these events -- handling them multiple times per runloop is noisy and expensive
|
|
||||||
_queuedRangeUpdate = YES;
|
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
[self performRangeUpdate];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)performRangeUpdate
|
|
||||||
{
|
|
||||||
// Call this version if you want the update to occur immediately, such as on app suspend, as another runloop may not occur.
|
|
||||||
ASDisplayNodeAssertMainThread();
|
|
||||||
_queuedRangeUpdate = YES; // For now, set this flag as _update... expects it and clears it.
|
|
||||||
[self _updateVisibleNodeIndexPaths];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setLayoutController:(id<ASLayoutController>)layoutController
|
- (void)setLayoutController:(id<ASLayoutController>)layoutController
|
||||||
{
|
{
|
||||||
_layoutController = layoutController;
|
_layoutController = layoutController;
|
||||||
_layoutControllerImplementsSetVisibleIndexPaths = [_layoutController respondsToSelector:@selector(setVisibleNodeIndexPaths:)];
|
_layoutControllerImplementsSetVisibleIndexPaths = [_layoutController respondsToSelector:@selector(setVisibleNodeIndexPaths:)];
|
||||||
if (_layoutController && _queuedRangeUpdate) {
|
if (layoutController && _dataSource) {
|
||||||
[self performRangeUpdate];
|
[self updateIfNeeded];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setDataSource:(id<ASRangeControllerDataSource>)dataSource
|
- (void)setDataSource:(id<ASRangeControllerDataSource>)dataSource
|
||||||
{
|
{
|
||||||
_dataSource = dataSource;
|
_dataSource = dataSource;
|
||||||
if (_dataSource && _queuedRangeUpdate) {
|
if (dataSource && _layoutController) {
|
||||||
[self performRangeUpdate];
|
[self updateIfNeeded];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_updateVisibleNodeIndexPaths
|
- (void)_updateVisibleNodeIndexPaths
|
||||||
{
|
{
|
||||||
ASDisplayNodeAssert(_layoutController, @"An ASLayoutController is required by ASRangeController");
|
ASDisplayNodeAssert(_layoutController, @"An ASLayoutController is required by ASRangeController");
|
||||||
if (!_queuedRangeUpdate || !_layoutController || !_dataSource) {
|
if (!_layoutController || !_dataSource) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if AS_RANGECONTROLLER_LOG_UPDATE_FREQ
|
||||||
|
_updateCountThisFrame += 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
// allNodes is a 2D array: it contains arrays for each section, each containing nodes.
|
// allNodes is a 2D array: it contains arrays for each section, each containing nodes.
|
||||||
NSArray<NSArray *> *allNodes = [_dataSource completedNodes];
|
NSArray<NSArray *> *allNodes = [_dataSource completedNodes];
|
||||||
NSUInteger numberOfSections = [allNodes count];
|
NSUInteger numberOfSections = [allNodes count];
|
||||||
|
|
||||||
// TODO: Consider if we need to use this codepath, or can rely on something more similar to the data & display ranges
|
// TODO: Consider if we need to use this codepath, or can rely on something more similar to the data & display ranges
|
||||||
// Example: ... = [_layoutController indexPathsForScrolling:_scrollDirection rangeType:ASLayoutRangeTypeVisible];
|
// Example: ... = [_layoutController indexPathsForScrolling:scrollDirection rangeType:ASLayoutRangeTypeVisible];
|
||||||
NSArray<NSIndexPath *> *visibleNodePaths = [_dataSource visibleNodeIndexPathsForRangeController:self];
|
NSArray<NSIndexPath *> *visibleNodePaths = [_dataSource visibleNodeIndexPathsForRangeController:self];
|
||||||
|
|
||||||
if (visibleNodePaths.count == 0) { // if we don't have any visibleNodes currently (scrolled before or after content)...
|
if (visibleNodePaths.count == 0) { // if we don't have any visibleNodes currently (scrolled before or after content)...
|
||||||
_queuedRangeUpdate = NO;
|
|
||||||
return; // don't do anything for this update, but leave _rangeIsValid == NO to make sure we update it later
|
return; // don't do anything for this update, but leave _rangeIsValid == NO to make sure we update it later
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ASScrollDirection scrollDirection = [_dataSource scrollDirectionForRangeController:self];
|
||||||
[_layoutController setViewportSize:[_dataSource viewportSizeForRangeController:self]];
|
[_layoutController setViewportSize:[_dataSource viewportSizeForRangeController:self]];
|
||||||
|
|
||||||
// the layout controller needs to know what the current visible indices are to calculate range offsets
|
// the layout controller needs to know what the current visible indices are to calculate range offsets
|
||||||
@@ -203,7 +213,7 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive;
|
|||||||
if (ASRangeTuningParametersEqualToRangeTuningParameters(parametersFetchData, ASRangeTuningParametersZero)) {
|
if (ASRangeTuningParametersEqualToRangeTuningParameters(parametersFetchData, ASRangeTuningParametersZero)) {
|
||||||
fetchDataIndexPaths = visibleIndexPaths;
|
fetchDataIndexPaths = visibleIndexPaths;
|
||||||
} else {
|
} else {
|
||||||
fetchDataIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection
|
fetchDataIndexPaths = [_layoutController indexPathsForScrolling:scrollDirection
|
||||||
rangeMode:rangeMode
|
rangeMode:rangeMode
|
||||||
rangeType:ASLayoutRangeTypeFetchData];
|
rangeType:ASLayoutRangeTypeFetchData];
|
||||||
}
|
}
|
||||||
@@ -217,7 +227,7 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive;
|
|||||||
} else if (ASRangeTuningParametersEqualToRangeTuningParameters(parametersDisplay, parametersFetchData)) {
|
} else if (ASRangeTuningParametersEqualToRangeTuningParameters(parametersDisplay, parametersFetchData)) {
|
||||||
displayIndexPaths = fetchDataIndexPaths;
|
displayIndexPaths = fetchDataIndexPaths;
|
||||||
} else {
|
} else {
|
||||||
displayIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection
|
displayIndexPaths = [_layoutController indexPathsForScrolling:scrollDirection
|
||||||
rangeMode:rangeMode
|
rangeMode:rangeMode
|
||||||
rangeType:ASLayoutRangeTypeDisplay];
|
rangeType:ASLayoutRangeTypeDisplay];
|
||||||
}
|
}
|
||||||
@@ -322,7 +332,6 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive;
|
|||||||
}
|
}
|
||||||
|
|
||||||
_rangeIsValid = YES;
|
_rangeIsValid = YES;
|
||||||
_queuedRangeUpdate = NO;
|
|
||||||
|
|
||||||
#if ASRangeControllerLoggingEnabled
|
#if ASRangeControllerLoggingEnabled
|
||||||
// NSSet *visibleNodePathsSet = [NSSet setWithArray:visibleNodePaths];
|
// NSSet *visibleNodePathsSet = [NSSet setWithArray:visibleNodePaths];
|
||||||
@@ -363,7 +372,7 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive;
|
|||||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:ASRenderingEngineDidDisplayScheduledNodesNotification object:nil];
|
[[NSNotificationCenter defaultCenter] removeObserver:self name:ASRenderingEngineDidDisplayScheduledNodesNotification object:nil];
|
||||||
_didRegisterForNodeDisplayNotifications = NO;
|
_didRegisterForNodeDisplayNotifications = NO;
|
||||||
|
|
||||||
[self scheduleRangeUpdate];
|
[self setNeedsUpdate];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -509,7 +518,8 @@ static ASLayoutRangeMode __rangeModeForMemoryWarnings = ASLayoutRangeModeVisible
|
|||||||
for (ASRangeController *rangeController in allRangeControllers) {
|
for (ASRangeController *rangeController in allRangeControllers) {
|
||||||
BOOL isDisplay = ASInterfaceStateIncludesDisplay([rangeController interfaceState]);
|
BOOL isDisplay = ASInterfaceStateIncludesDisplay([rangeController interfaceState]);
|
||||||
[rangeController updateCurrentRangeWithMode:isDisplay ? ASLayoutRangeModeMinimum : __rangeModeForMemoryWarnings];
|
[rangeController updateCurrentRangeWithMode:isDisplay ? ASLayoutRangeModeMinimum : __rangeModeForMemoryWarnings];
|
||||||
[rangeController performRangeUpdate];
|
[rangeController setNeedsUpdate];
|
||||||
|
[rangeController updateIfNeeded];
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ASRangeControllerLoggingEnabled
|
#if ASRangeControllerLoggingEnabled
|
||||||
@@ -531,7 +541,8 @@ static ASLayoutRangeMode __rangeModeForMemoryWarnings = ASLayoutRangeModeVisible
|
|||||||
__ApplicationState = UIApplicationStateBackground;
|
__ApplicationState = UIApplicationStateBackground;
|
||||||
for (ASRangeController *rangeController in allRangeControllers) {
|
for (ASRangeController *rangeController in allRangeControllers) {
|
||||||
// Trigger a range update immediately, as we may not be allowed by the system to run the update block scheduled by changing range mode.
|
// Trigger a range update immediately, as we may not be allowed by the system to run the update block scheduled by changing range mode.
|
||||||
[rangeController performRangeUpdate];
|
[rangeController setNeedsUpdate];
|
||||||
|
[rangeController updateIfNeeded];
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ASRangeControllerLoggingEnabled
|
#if ASRangeControllerLoggingEnabled
|
||||||
@@ -546,7 +557,8 @@ static ASLayoutRangeMode __rangeModeForMemoryWarnings = ASLayoutRangeModeVisible
|
|||||||
for (ASRangeController *rangeController in allRangeControllers) {
|
for (ASRangeController *rangeController in allRangeControllers) {
|
||||||
BOOL isVisible = ASInterfaceStateIncludesVisible([rangeController interfaceState]);
|
BOOL isVisible = ASInterfaceStateIncludesVisible([rangeController interfaceState]);
|
||||||
[rangeController updateCurrentRangeWithMode:isVisible ? ASLayoutRangeModeMinimum : ASLayoutRangeModeVisibleOnly];
|
[rangeController updateCurrentRangeWithMode:isVisible ? ASLayoutRangeModeMinimum : ASLayoutRangeModeVisibleOnly];
|
||||||
[rangeController performRangeUpdate];
|
[rangeController setNeedsUpdate];
|
||||||
|
[rangeController updateIfNeeded];
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ASRangeControllerLoggingEnabled
|
#if ASRangeControllerLoggingEnabled
|
||||||
@@ -556,6 +568,16 @@ static ASLayoutRangeMode __rangeModeForMemoryWarnings = ASLayoutRangeModeVisible
|
|||||||
|
|
||||||
#pragma mark - Debugging
|
#pragma mark - Debugging
|
||||||
|
|
||||||
|
#if AS_RANGECONTROLLER_LOG_UPDATE_FREQ
|
||||||
|
- (void)_updateCountDisplayLinkDidFire
|
||||||
|
{
|
||||||
|
if (_updateCountThisFrame > 1) {
|
||||||
|
NSLog(@"ASRangeController %p updated %lu times this frame.", self, (unsigned long)_updateCountThisFrame);
|
||||||
|
}
|
||||||
|
_updateCountThisFrame = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
- (NSString *)descriptionWithIndexPaths:(NSArray<NSIndexPath *> *)indexPaths
|
- (NSString *)descriptionWithIndexPaths:(NSArray<NSIndexPath *> *)indexPaths
|
||||||
{
|
{
|
||||||
NSMutableString *description = [NSMutableString stringWithFormat:@"%@ %@", [super description], @" allPreviousIndexPaths:\n"];
|
NSMutableString *description = [NSMutableString stringWithFormat:@"%@ %@", [super description], @" allPreviousIndexPaths:\n"];
|
||||||
|
|||||||
@@ -13,7 +13,8 @@
|
|||||||
@protocol ASRangeControllerUpdateRangeProtocol <NSObject>
|
@protocol ASRangeControllerUpdateRangeProtocol <NSObject>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the current range mode of the range controller for at least the next range update.
|
* Updates the current range mode of the range controller for at least the next range update
|
||||||
|
* and, if the new mode is different from the previous mode, enqueues a range update.
|
||||||
*/
|
*/
|
||||||
- (void)updateCurrentRangeWithMode:(ASLayoutRangeMode)rangeMode;
|
- (void)updateCurrentRangeWithMode:(ASLayoutRangeMode)rangeMode;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user