mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 22:25:57 +00:00
[ASRangeController] Update synchronously when possible
This commit is contained in:
committed by
Adlai Holler
parent
2e3da9bc92
commit
edb4e45c24
@@ -23,6 +23,7 @@
|
||||
#import "ASLayout.h"
|
||||
#import "_ASDisplayLayer.h"
|
||||
#import "ASTableNode.h"
|
||||
#import "ASEqualityHelpers.h"
|
||||
|
||||
static const ASSizeRange kInvalidSizeRange = {CGSizeZero, CGSizeZero};
|
||||
static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
@@ -126,6 +127,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
BOOL _ignoreNodesConstrainedWidthChange;
|
||||
BOOL _queuedNodeHeightUpdate;
|
||||
BOOL _isDeallocating;
|
||||
BOOL _performingBatchUpdates;
|
||||
NSMutableSet *_cellsForVisibilityUpdates;
|
||||
|
||||
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
|
||||
[super layoutSubviews];
|
||||
[_rangeController updateIfNeeded];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
@@ -644,35 +647,29 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
[_asyncDelegate tableView:self willDisplayNodeForRowAtIndexPath:indexPath];
|
||||
}
|
||||
|
||||
[_rangeController visibleNodeIndexPathsDidChangeWithScrollDirection:[self scrollDirection]];
|
||||
|
||||
if (cellNode.neverShowPlaceholders) {
|
||||
[cellNode recursivelyEnsureDisplaySynchronously:YES];
|
||||
}
|
||||
[_rangeController setNeedsUpdate];
|
||||
|
||||
if (ASSubclassOverridesSelector([ASCellNode class], [cellNode class], @selector(cellNodeVisibilityEvent:inScrollView:withCellFrame:))) {
|
||||
[_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;
|
||||
}
|
||||
|
||||
ASCellNode *cellNode = [cell node];
|
||||
|
||||
[_rangeController visibleNodeIndexPathsDidChangeWithScrollDirection:[self scrollDirection]];
|
||||
[_rangeController setNeedsUpdate];
|
||||
|
||||
if (_asyncDelegateFlags.asyncDelegateTableViewDidEndDisplayingNodeForRowAtIndexPath) {
|
||||
ASDisplayNodeAssertNotNil(cellNode, @"Expected node associated with removed cell not to be nil.");
|
||||
[_asyncDelegate tableView:self didEndDisplayingNode:cellNode forRowAtIndexPath:indexPath];
|
||||
}
|
||||
|
||||
if ([_cellsForVisibilityUpdates containsObject:cell]) {
|
||||
[_cellsForVisibilityUpdates removeObject:cell];
|
||||
}
|
||||
[_cellsForVisibilityUpdates removeObject:cell];
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
@@ -866,61 +863,38 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
return @[];
|
||||
}
|
||||
|
||||
// In this case we cannot use indexPathsForVisibleRows in this case to get all the visible index paths as apparently
|
||||
// in a grouped UITableView it would return index paths for cells that are over the edge of the visible area.
|
||||
// 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
|
||||
NSMutableArray *visibleIndexPaths = [NSMutableArray array];
|
||||
for (id cell in self.visibleCells) {
|
||||
[visibleIndexPaths addObject:[self indexPathForCell:cell]];
|
||||
// NOTE: A prior comment claimed that `indexPathsForVisibleRows` may return extra index paths for grouped-style
|
||||
// tables. This is seen as an acceptable issue for the time being.
|
||||
|
||||
NSIndexPath *pendingVisibleIndexPath = _pendingVisibleIndexPath;
|
||||
if (pendingVisibleIndexPath == nil) {
|
||||
return self.indexPathsForVisibleRows;
|
||||
}
|
||||
|
||||
if (_pendingVisibleIndexPath) {
|
||||
NSMutableSet *indexPaths = [NSMutableSet setWithArray:visibleIndexPaths];
|
||||
|
||||
BOOL (^isAfter)(NSIndexPath *, NSIndexPath *) = ^BOOL(NSIndexPath *indexPath, 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;
|
||||
};
|
||||
|
||||
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
|
||||
} else if (!isBefore(_pendingVisibleIndexPath, visibleIndexPaths.firstObject) &&
|
||||
!isAfter(_pendingVisibleIndexPath, visibleIndexPaths.lastObject)) {
|
||||
_pendingVisibleIndexPath = nil; // not contiguous, ignore.
|
||||
} else {
|
||||
[indexPaths addObject:_pendingVisibleIndexPath];
|
||||
|
||||
[visibleIndexPaths removeAllObjects];
|
||||
[visibleIndexPaths addObjectsFromArray:[indexPaths.allObjects sortedArrayUsingSelector:@selector(compare:)]];
|
||||
}
|
||||
}
|
||||
NSMutableArray *visibleIndexPaths = [self.indexPathsForVisibleRows mutableCopy];
|
||||
[visibleIndexPaths sortUsingSelector:@selector(compare:)];
|
||||
|
||||
BOOL isPendingIndexPathVisible = (NSNotFound != [visibleIndexPaths indexOfObject:pendingVisibleIndexPath inSortedRange:NSMakeRange(0, visibleIndexPaths.count) options:kNilOptions usingComparator:^(id _Nonnull obj1, id _Nonnull obj2) {
|
||||
return [obj1 compare:obj2];
|
||||
}]);
|
||||
|
||||
if (isPendingIndexPathVisible) {
|
||||
_pendingVisibleIndexPath = nil; // once it has shown up in visibleIndexPaths, we can stop tracking it
|
||||
} else if ([self isIndexPath:visibleIndexPaths.firstObject immediateSuccessorOfIndexPath:pendingVisibleIndexPath]) {
|
||||
[visibleIndexPaths insertObject:pendingVisibleIndexPath atIndex:0];
|
||||
} else if ([self isIndexPath:pendingVisibleIndexPath immediateSuccessorOfIndexPath:visibleIndexPaths.lastObject]) {
|
||||
[visibleIndexPaths addObject:pendingVisibleIndexPath];
|
||||
} else {
|
||||
_pendingVisibleIndexPath = nil; // not contiguous, ignore.
|
||||
}
|
||||
return visibleIndexPaths;
|
||||
}
|
||||
|
||||
- (ASScrollDirection)scrollDirectionForRangeController:(ASRangeController *)rangeController
|
||||
{
|
||||
return self.scrollDirection;
|
||||
}
|
||||
|
||||
- (NSArray *)rangeController:(ASRangeController *)rangeController nodesAtIndexPaths:(NSArray *)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
|
||||
}
|
||||
|
||||
_performingBatchUpdates = YES;
|
||||
[super beginUpdates];
|
||||
|
||||
if (_automaticallyAdjustsContentOffset) {
|
||||
@@ -978,8 +953,10 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
|
||||
ASPerformBlockWithoutAnimation(!animated, ^{
|
||||
[super endUpdates];
|
||||
[_rangeController updateIfNeeded];
|
||||
});
|
||||
|
||||
_performingBatchUpdates = NO;
|
||||
if (completion) {
|
||||
completion(YES);
|
||||
}
|
||||
@@ -1005,6 +982,9 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
NSLog(@"-[super insertRowsAtIndexPaths]: %@", indexPaths);
|
||||
}
|
||||
[super insertRowsAtIndexPaths:indexPaths withRowAnimation:(UITableViewRowAnimation)animationOptions];
|
||||
if (!_performingBatchUpdates) {
|
||||
[_rangeController updateIfNeeded];
|
||||
}
|
||||
[self _scheduleCheckForBatchFetchingForNumberOfChanges:indexPaths.count];
|
||||
});
|
||||
|
||||
@@ -1028,6 +1008,9 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
NSLog(@"-[super deleteRowsAtIndexPaths]: %@", indexPaths);
|
||||
}
|
||||
[super deleteRowsAtIndexPaths:indexPaths withRowAnimation:(UITableViewRowAnimation)animationOptions];
|
||||
if (!_performingBatchUpdates) {
|
||||
[_rangeController updateIfNeeded];
|
||||
}
|
||||
[self _scheduleCheckForBatchFetchingForNumberOfChanges:indexPaths.count];
|
||||
});
|
||||
|
||||
@@ -1052,6 +1035,9 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
NSLog(@"-[super insertSections]: %@", indexSet);
|
||||
}
|
||||
[super insertSections:indexSet withRowAnimation:(UITableViewRowAnimation)animationOptions];
|
||||
if (!_performingBatchUpdates) {
|
||||
[_rangeController updateIfNeeded];
|
||||
}
|
||||
[self _scheduleCheckForBatchFetchingForNumberOfChanges:indexSet.count];
|
||||
});
|
||||
}
|
||||
@@ -1071,6 +1057,9 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
NSLog(@"-[super deleteSections]: %@", indexSet);
|
||||
}
|
||||
[super deleteSections:indexSet withRowAnimation:(UITableViewRowAnimation)animationOptions];
|
||||
if (!_performingBatchUpdates) {
|
||||
[_rangeController updateIfNeeded];
|
||||
}
|
||||
[self _scheduleCheckForBatchFetchingForNumberOfChanges:indexSet.count];
|
||||
});
|
||||
}
|
||||
@@ -1232,6 +1221,32 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
[_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
|
||||
// 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.
|
||||
@@ -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
|
||||
// their update in the layout pass
|
||||
if (![node supportsRangeManagedInterfaceState]) {
|
||||
[_rangeController visibleNodeIndexPathsDidChangeWithScrollDirection:[self scrollDirection]];
|
||||
[_rangeController setNeedsUpdate];
|
||||
[_rangeController updateIfNeeded];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user