mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-15 10:53:27 +00:00
Move most of the batch fetching logic to a central place for ASTableView and ASCollectionView usage
This commit is contained in:
parent
40fe1f3ac7
commit
24ca09ee6c
@ -91,7 +91,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
#pragma mark -
|
#pragma mark -
|
||||||
#pragma mark ASCollectionView.
|
#pragma mark ASCollectionView.
|
||||||
|
|
||||||
@interface ASCollectionView () <ASRangeControllerDataSource, ASRangeControllerDelegate, ASDataControllerSource, ASCellNodeLayoutDelegate, ASDelegateProxyInterceptor> {
|
@interface ASCollectionView () <ASRangeControllerDataSource, ASRangeControllerDelegate, ASDataControllerSource, ASCellNodeLayoutDelegate, ASDelegateProxyInterceptor, ASBatchFetchingScrollView> {
|
||||||
ASCollectionViewProxy *_proxyDataSource;
|
ASCollectionViewProxy *_proxyDataSource;
|
||||||
ASCollectionViewProxy *_proxyDelegate;
|
ASCollectionViewProxy *_proxyDelegate;
|
||||||
|
|
||||||
@ -605,8 +605,45 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
cellNode.scrollView = nil;
|
cellNode.scrollView = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Scroll Direction.
|
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
|
||||||
|
{
|
||||||
|
// If a scroll happenes the current range mode needs to go to full
|
||||||
|
ASInterfaceState interfaceState = [self interfaceStateForRangeController:_rangeController];
|
||||||
|
if (ASInterfaceStateIncludesVisible(interfaceState)) {
|
||||||
|
[_rangeController updateCurrentRangeWithMode:ASLayoutRangeModeFull];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (_ASCollectionViewCell *collectionCell in _cellsForVisibilityUpdates) {
|
||||||
|
// Only nodes that respond to the selector are added to _cellsForVisibilityUpdates
|
||||||
|
[[collectionCell node] cellNodeVisibilityEvent:ASCellNodeVisibilityEventVisibleRectChanged
|
||||||
|
inScrollView:scrollView
|
||||||
|
withCellFrame:collectionCell.frame];
|
||||||
|
}
|
||||||
|
if (_asyncDelegateImplementsScrollviewDidScroll) {
|
||||||
|
[_asyncDelegate scrollViewDidScroll:scrollView];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
|
||||||
|
{
|
||||||
|
_deceleratingVelocity = CGPointMake(
|
||||||
|
scrollView.contentOffset.x - ((targetContentOffset != NULL) ? targetContentOffset->x : 0),
|
||||||
|
scrollView.contentOffset.y - ((targetContentOffset != NULL) ? targetContentOffset->y : 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (targetContentOffset != NULL) {
|
||||||
|
ASDisplayNodeAssert(_batchContext != nil, @"Batch context should exist");
|
||||||
|
[self _beginBatchFetchingIfNeededWithScrollView:self forScrollDirection:[self scrollDirection] contentOffset:*targetContentOffset];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([_asyncDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]) {
|
||||||
|
[_asyncDelegate scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#pragma mark - Scroll Direction.
|
||||||
|
|
||||||
- (ASScrollDirection)scrollDirection
|
- (ASScrollDirection)scrollDirection
|
||||||
{
|
{
|
||||||
@ -700,58 +737,14 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
#pragma mark - Batch Fetching
|
||||||
#pragma mark Batch Fetching
|
|
||||||
|
|
||||||
- (void)_checkForBatchFetching
|
- (ASBatchContext *)batchContext
|
||||||
{
|
{
|
||||||
// Dragging will be handled in scrollViewWillEndDragging:withVelocity:targetContentOffset:
|
return _batchContext;
|
||||||
if ([self isDragging] || [self isTracking] || ![self _shouldBatchFetch]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we should batch fetch
|
|
||||||
if (ASDisplayShouldFetchBatchForContext(_batchContext, [self scrollableDirections], self.bounds, self.contentSize, self.contentOffset, _leadingScreensForBatching)) {
|
|
||||||
[self _beginBatchFetching];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
|
- (BOOL)canBatchFetch
|
||||||
{
|
|
||||||
_deceleratingVelocity = CGPointMake(
|
|
||||||
scrollView.contentOffset.x - ((targetContentOffset != NULL) ? targetContentOffset->x : 0),
|
|
||||||
scrollView.contentOffset.y - ((targetContentOffset != NULL) ? targetContentOffset->y : 0)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (targetContentOffset != NULL) {
|
|
||||||
[self _handleBatchFetchScrollingToOffset:*targetContentOffset];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([_asyncDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]) {
|
|
||||||
[_asyncDelegate scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
|
|
||||||
{
|
|
||||||
// If a scroll happenes the current range mode needs to go to full
|
|
||||||
ASInterfaceState interfaceState = [self interfaceStateForRangeController:_rangeController];
|
|
||||||
if (ASInterfaceStateIncludesVisible(interfaceState)) {
|
|
||||||
[_rangeController updateCurrentRangeWithMode:ASLayoutRangeModeFull];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (_ASCollectionViewCell *collectionCell in _cellsForVisibilityUpdates) {
|
|
||||||
// Only nodes that respond to the selector are added to _cellsForVisibilityUpdates
|
|
||||||
[[collectionCell node] cellNodeVisibilityEvent:ASCellNodeVisibilityEventVisibleRectChanged
|
|
||||||
inScrollView:scrollView
|
|
||||||
withCellFrame:collectionCell.frame];
|
|
||||||
}
|
|
||||||
if (_asyncDelegateImplementsScrollviewDidScroll) {
|
|
||||||
[_asyncDelegate scrollViewDidScroll:scrollView];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)_shouldBatchFetch
|
|
||||||
{
|
{
|
||||||
// if the delegate does not respond to this method, there is no point in starting to fetch
|
// if the delegate does not respond to this method, there is no point in starting to fetch
|
||||||
BOOL canFetch = [_asyncDelegate respondsToSelector:@selector(collectionView:willBeginBatchFetchWithContext:)];
|
BOOL canFetch = [_asyncDelegate respondsToSelector:@selector(collectionView:willBeginBatchFetchWithContext:)];
|
||||||
@ -762,25 +755,41 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_handleBatchFetchScrollingToOffset:(CGPoint)targetOffset
|
- (void)_scheduleCheckForBatchFetching
|
||||||
{
|
{
|
||||||
ASDisplayNodeAssert(_batchContext != nil, @"Batch context should exist");
|
// Push this to the next runloop to be sure the UITableView has the right content size
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[self _checkForBatchFetching];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (![self _shouldBatchFetch]) {
|
- (void)_checkForBatchFetching
|
||||||
|
{
|
||||||
|
// Dragging will be handled in scrollViewWillEndDragging:withVelocity:targetContentOffset:
|
||||||
|
if (self.isDragging || self.isTracking) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ASDisplayShouldFetchBatchForContext(_batchContext, [self scrollDirection], self.bounds, self.contentSize, targetOffset, _leadingScreensForBatching)) {
|
[self _beginBatchFetchingIfNeededWithScrollView:self forScrollDirection:[self scrollableDirections] contentOffset:self.contentOffset];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)_beginBatchFetchingIfNeededWithScrollView:(UIScrollView<ASBatchFetchingScrollView> *)scrollView forScrollDirection:(ASScrollDirection)scrollDirection contentOffset:(CGPoint)contentOffset
|
||||||
|
{
|
||||||
|
if (ASDisplayShouldFetchBatchForScrollView(self, scrollDirection, contentOffset)) {
|
||||||
[self _beginBatchFetching];
|
[self _beginBatchFetching];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_beginBatchFetching
|
- (void)_beginBatchFetching
|
||||||
{
|
{
|
||||||
|
NSLog(@"begin batch fetching");
|
||||||
|
|
||||||
[_batchContext beginBatchFetching];
|
[_batchContext beginBatchFetching];
|
||||||
|
if ([_asyncDelegate respondsToSelector:@selector(collectionView:willBeginBatchFetchWithContext:)]) {
|
||||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
[_asyncDelegate collectionView:self willBeginBatchFetchWithContext:_batchContext];
|
[_asyncDelegate collectionView:self willBeginBatchFetchWithContext:_batchContext];
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -972,7 +981,10 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
for (dispatch_block_t block in _batchUpdateBlocks) {
|
for (dispatch_block_t block in _batchUpdateBlocks) {
|
||||||
block();
|
block();
|
||||||
}
|
}
|
||||||
} completion:completion];
|
} completion:^(BOOL finished){
|
||||||
|
[self _scheduleCheckForBatchFetching];
|
||||||
|
if (completion) { completion(finished); }
|
||||||
|
}];
|
||||||
});
|
});
|
||||||
|
|
||||||
[_batchUpdateBlocks removeAllObjects];
|
[_batchUpdateBlocks removeAllObjects];
|
||||||
@ -993,11 +1005,10 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
}];
|
}];
|
||||||
} else {
|
} else {
|
||||||
[_layoutFacilitator collectionViewWillEditCellsAtIndexPaths:indexPaths batched:NO];
|
[_layoutFacilitator collectionViewWillEditCellsAtIndexPaths:indexPaths batched:NO];
|
||||||
ASPerformBlockWithoutAnimationCompletion(YES, ^{
|
[UIView performWithoutAnimation:^{
|
||||||
[super insertItemsAtIndexPaths:indexPaths];
|
[super insertItemsAtIndexPaths:indexPaths];
|
||||||
}, ^{
|
[self _scheduleCheckForBatchFetching];
|
||||||
|
}];
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1017,6 +1028,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
[_layoutFacilitator collectionViewWillEditCellsAtIndexPaths:indexPaths batched:NO];
|
[_layoutFacilitator collectionViewWillEditCellsAtIndexPaths:indexPaths batched:NO];
|
||||||
[UIView performWithoutAnimation:^{
|
[UIView performWithoutAnimation:^{
|
||||||
[super deleteItemsAtIndexPaths:indexPaths];
|
[super deleteItemsAtIndexPaths:indexPaths];
|
||||||
|
[self _scheduleCheckForBatchFetching];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1037,6 +1049,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
[_layoutFacilitator collectionViewWillEditSectionsAtIndexSet:indexSet batched:NO];
|
[_layoutFacilitator collectionViewWillEditSectionsAtIndexSet:indexSet batched:NO];
|
||||||
[UIView performWithoutAnimation:^{
|
[UIView performWithoutAnimation:^{
|
||||||
[super insertSections:indexSet];
|
[super insertSections:indexSet];
|
||||||
|
[self _scheduleCheckForBatchFetching];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1057,6 +1070,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
[_layoutFacilitator collectionViewWillEditSectionsAtIndexSet:indexSet batched:NO];
|
[_layoutFacilitator collectionViewWillEditSectionsAtIndexSet:indexSet batched:NO];
|
||||||
[UIView performWithoutAnimation:^{
|
[UIView performWithoutAnimation:^{
|
||||||
[super deleteSections:indexSet];
|
[super deleteSections:indexSet];
|
||||||
|
[self _scheduleCheckForBatchFetching];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -88,9 +88,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
- (instancetype)_initWithTableView:(ASTableView *)tableView;
|
- (instancetype)_initWithTableView:(ASTableView *)tableView;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface ASTableView () <ASRangeControllerDataSource, ASRangeControllerDelegate,
|
@interface ASTableView () <ASRangeControllerDataSource, ASRangeControllerDelegate, ASDataControllerSource, _ASTableViewCellDelegate, ASCellNodeLayoutDelegate, ASDelegateProxyInterceptor, ASBatchFetchingScrollView>
|
||||||
ASDataControllerSource, _ASTableViewCellDelegate,
|
|
||||||
ASCellNodeLayoutDelegate, ASDelegateProxyInterceptor>
|
|
||||||
{
|
{
|
||||||
ASTableViewProxy *_proxyDataSource;
|
ASTableViewProxy *_proxyDataSource;
|
||||||
ASTableViewProxy *_proxyDelegate;
|
ASTableViewProxy *_proxyDelegate;
|
||||||
@ -524,8 +522,8 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Intercepted selectors
|
#pragma mark - Intercepted selectors
|
||||||
|
|
||||||
- (void)setTableHeaderView:(UIView *)tableHeaderView
|
- (void)setTableHeaderView:(UIView *)tableHeaderView
|
||||||
{
|
{
|
||||||
@ -579,75 +577,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
return [_dataController numberOfRowsInSection:section];
|
return [_dataController numberOfRowsInSection:section];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (ASScrollDirection)scrollDirection
|
|
||||||
{
|
|
||||||
CGPoint scrollVelocity;
|
|
||||||
if (self.isTracking) {
|
|
||||||
scrollVelocity = [self.panGestureRecognizer velocityInView:self.superview];
|
|
||||||
} else {
|
|
||||||
scrollVelocity = _deceleratingVelocity;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASScrollDirection scrollDirection = [self _scrollDirectionForVelocity:scrollVelocity];
|
|
||||||
return ASScrollDirectionApplyTransform(scrollDirection, self.transform);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (ASScrollDirection)scrollableDirections
|
|
||||||
{
|
|
||||||
ASScrollDirection scrollableDirection = ASScrollDirectionNone;
|
|
||||||
CGFloat totalContentWidth = self.contentSize.width + self.contentInset.left + self.contentInset.right;
|
|
||||||
CGFloat totalContentHeight = self.contentSize.height + self.contentInset.top + self.contentInset.bottom;
|
|
||||||
|
|
||||||
if (self.alwaysBounceHorizontal || totalContentWidth > self.bounds.size.width) { // Can scroll horizontally.
|
|
||||||
scrollableDirection |= ASScrollDirectionHorizontalDirections;
|
|
||||||
}
|
|
||||||
if (self.alwaysBounceVertical || totalContentHeight > self.bounds.size.height) { // Can scroll vertically.
|
|
||||||
scrollableDirection |= ASScrollDirectionVerticalDirections;
|
|
||||||
}
|
|
||||||
return scrollableDirection;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (ASScrollDirection)_scrollDirectionForVelocity:(CGPoint)scrollVelocity
|
|
||||||
{
|
|
||||||
ASScrollDirection direction = ASScrollDirectionNone;
|
|
||||||
ASScrollDirection scrollableDirections = [self scrollableDirections];
|
|
||||||
|
|
||||||
if (ASScrollDirectionContainsHorizontalDirection(scrollableDirections)) { // Can scroll horizontally.
|
|
||||||
if (scrollVelocity.x < 0.0) {
|
|
||||||
direction |= ASScrollDirectionRight;
|
|
||||||
} else if (scrollVelocity.x > 0.0) {
|
|
||||||
direction |= ASScrollDirectionLeft;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ASScrollDirectionContainsVerticalDirection(scrollableDirections)) { // Can scroll vertically.
|
|
||||||
if (scrollVelocity.y < 0.0) {
|
|
||||||
direction |= ASScrollDirectionDown;
|
|
||||||
} else if (scrollVelocity.y > 0.0) {
|
|
||||||
direction |= ASScrollDirectionUp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return direction;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
|
|
||||||
{
|
|
||||||
// If a scroll happenes the current range mode needs to go to full
|
|
||||||
ASInterfaceState interfaceState = [self interfaceStateForRangeController:_rangeController];
|
|
||||||
if (ASInterfaceStateIncludesVisible(interfaceState)) {
|
|
||||||
[_rangeController updateCurrentRangeWithMode:ASLayoutRangeModeFull];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (_ASTableViewCell *tableCell in _cellsForVisibilityUpdates) {
|
|
||||||
[[tableCell node] cellNodeVisibilityEvent:ASCellNodeVisibilityEventVisibleRectChanged
|
|
||||||
inScrollView:scrollView
|
|
||||||
withCellFrame:tableCell.frame];
|
|
||||||
}
|
|
||||||
if (_asyncDelegateImplementsScrollviewDidScroll) {
|
|
||||||
[_asyncDelegate scrollViewDidScroll:scrollView];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)tableView:(UITableView *)tableView willDisplayCell:(_ASTableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
|
- (void)tableView:(UITableView *)tableView willDisplayCell:(_ASTableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
|
||||||
{
|
{
|
||||||
_pendingVisibleIndexPath = indexPath;
|
_pendingVisibleIndexPath = indexPath;
|
||||||
@ -700,16 +629,22 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark - Batch Fetching
|
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
|
||||||
|
|
||||||
- (void)_checkForBatchFetching
|
|
||||||
{
|
{
|
||||||
// Dragging will be handled in scrollViewWillEndDragging:withVelocity:targetContentOffset:
|
// If a scroll happenes the current range mode needs to go to full
|
||||||
if ([self isDragging] || [self isTracking]) {
|
ASInterfaceState interfaceState = [self interfaceStateForRangeController:_rangeController];
|
||||||
return;
|
if (ASInterfaceStateIncludesVisible(interfaceState)) {
|
||||||
|
[_rangeController updateCurrentRangeWithMode:ASLayoutRangeModeFull];
|
||||||
}
|
}
|
||||||
|
|
||||||
[self _beginBatchFetchingIfNeededForScrollDirection:[self scrollableDirections] contentOffset:self.contentOffset];
|
for (_ASTableViewCell *tableCell in _cellsForVisibilityUpdates) {
|
||||||
|
[[tableCell node] cellNodeVisibilityEvent:ASCellNodeVisibilityEventVisibleRectChanged
|
||||||
|
inScrollView:scrollView
|
||||||
|
withCellFrame:tableCell.frame];
|
||||||
|
}
|
||||||
|
if (_asyncDelegateImplementsScrollviewDidScroll) {
|
||||||
|
[_asyncDelegate scrollViewDidScroll:scrollView];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
|
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
|
||||||
@ -721,7 +656,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
|
|
||||||
if (targetContentOffset != NULL) {
|
if (targetContentOffset != NULL) {
|
||||||
ASDisplayNodeAssert(_batchContext != nil, @"Batch context should exist");
|
ASDisplayNodeAssert(_batchContext != nil, @"Batch context should exist");
|
||||||
[self _beginBatchFetchingIfNeededForScrollDirection:[self scrollDirection] contentOffset:*targetContentOffset];
|
[self _beginBatchFetchingIfNeededWithScrollView:self forScrollDirection:[self scrollDirection] contentOffset:*targetContentOffset];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([_asyncDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]) {
|
if ([_asyncDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]) {
|
||||||
@ -729,19 +664,69 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_beginBatchFetchingIfNeededForScrollDirection:(ASScrollDirection)scrollDirection contentOffset:(CGPoint)contentOffset
|
|
||||||
|
#pragma mark - Scroll Direction
|
||||||
|
|
||||||
|
- (ASScrollDirection)scrollDirection
|
||||||
{
|
{
|
||||||
if (![self _shouldBatchFetch]) {
|
CGPoint scrollVelocity;
|
||||||
return;
|
if (self.isTracking) {
|
||||||
|
scrollVelocity = [self.panGestureRecognizer velocityInView:self.superview];
|
||||||
|
} else {
|
||||||
|
scrollVelocity = _deceleratingVelocity;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we should batch fetch
|
ASScrollDirection scrollDirection = [self _scrollDirectionForVelocity:scrollVelocity];
|
||||||
if (ASDisplayShouldFetchBatchForContext(_batchContext, [self scrollableDirections], self.bounds, self.contentSize, self.contentOffset, _leadingScreensForBatching)) {
|
return ASScrollDirectionApplyTransform(scrollDirection, self.transform);
|
||||||
[self _beginBatchFetching];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)_shouldBatchFetch
|
- (ASScrollDirection)_scrollDirectionForVelocity:(CGPoint)scrollVelocity
|
||||||
|
{
|
||||||
|
ASScrollDirection direction = ASScrollDirectionNone;
|
||||||
|
ASScrollDirection scrollableDirections = [self scrollableDirections];
|
||||||
|
|
||||||
|
if (ASScrollDirectionContainsHorizontalDirection(scrollableDirections)) { // Can scroll horizontally.
|
||||||
|
if (scrollVelocity.x < 0.0) {
|
||||||
|
direction |= ASScrollDirectionRight;
|
||||||
|
} else if (scrollVelocity.x > 0.0) {
|
||||||
|
direction |= ASScrollDirectionLeft;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ASScrollDirectionContainsVerticalDirection(scrollableDirections)) { // Can scroll vertically.
|
||||||
|
if (scrollVelocity.y < 0.0) {
|
||||||
|
direction |= ASScrollDirectionDown;
|
||||||
|
} else if (scrollVelocity.y > 0.0) {
|
||||||
|
direction |= ASScrollDirectionUp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (ASScrollDirection)scrollableDirections
|
||||||
|
{
|
||||||
|
ASScrollDirection scrollableDirection = ASScrollDirectionNone;
|
||||||
|
CGFloat totalContentWidth = self.contentSize.width + self.contentInset.left + self.contentInset.right;
|
||||||
|
CGFloat totalContentHeight = self.contentSize.height + self.contentInset.top + self.contentInset.bottom;
|
||||||
|
|
||||||
|
if (self.alwaysBounceHorizontal || totalContentWidth > self.bounds.size.width) { // Can scroll horizontally.
|
||||||
|
scrollableDirection |= ASScrollDirectionHorizontalDirections;
|
||||||
|
}
|
||||||
|
if (self.alwaysBounceVertical || totalContentHeight > self.bounds.size.height) { // Can scroll vertically.
|
||||||
|
scrollableDirection |= ASScrollDirectionVerticalDirections;
|
||||||
|
}
|
||||||
|
return scrollableDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#pragma mark - Batch Fetching
|
||||||
|
|
||||||
|
- (ASBatchContext *)batchContext
|
||||||
|
{
|
||||||
|
return _batchContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)canBatchFetch
|
||||||
{
|
{
|
||||||
// if the delegate does not respond to this method, there is no point in starting to fetch
|
// if the delegate does not respond to this method, there is no point in starting to fetch
|
||||||
BOOL canFetch = [_asyncDelegate respondsToSelector:@selector(tableView:willBeginBatchFetchWithContext:)];
|
BOOL canFetch = [_asyncDelegate respondsToSelector:@selector(tableView:willBeginBatchFetchWithContext:)];
|
||||||
@ -752,12 +737,39 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)_scheduleCheckForBatchFetching
|
||||||
|
{
|
||||||
|
// Push this to the next runloop to be sure the UITableView has the right content size
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[self _checkForBatchFetching];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)_checkForBatchFetching
|
||||||
|
{
|
||||||
|
// Dragging will be handled in scrollViewWillEndDragging:withVelocity:targetContentOffset:
|
||||||
|
if (self.isDragging || self.isTracking) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
[self _beginBatchFetchingIfNeededWithScrollView:self forScrollDirection:[self scrollableDirections] contentOffset:self.contentOffset];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)_beginBatchFetchingIfNeededWithScrollView:(UIScrollView<ASBatchFetchingScrollView> *)scrollView forScrollDirection:(ASScrollDirection)scrollDirection contentOffset:(CGPoint)contentOffset
|
||||||
|
{
|
||||||
|
if (ASDisplayShouldFetchBatchForScrollView(self, scrollDirection, contentOffset)) {
|
||||||
|
[self _beginBatchFetching];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (void)_beginBatchFetching
|
- (void)_beginBatchFetching
|
||||||
{
|
{
|
||||||
[_batchContext beginBatchFetching];
|
[_batchContext beginBatchFetching];
|
||||||
|
if ([_asyncDelegate respondsToSelector:@selector(tableView:willBeginBatchFetchWithContext:)]) {
|
||||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
[_asyncDelegate tableView:self willBeginBatchFetchWithContext:_batchContext];
|
[_asyncDelegate tableView:self willBeginBatchFetchWithContext:_batchContext];
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - ASRangeControllerDataSource
|
#pragma mark - ASRangeControllerDataSource
|
||||||
@ -897,13 +909,9 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
}
|
}
|
||||||
|
|
||||||
BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone;
|
BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone;
|
||||||
|
|
||||||
ASPerformBlockWithoutAnimation(preventAnimation, ^{
|
ASPerformBlockWithoutAnimation(preventAnimation, ^{
|
||||||
[super insertRowsAtIndexPaths:indexPaths withRowAnimation:(UITableViewRowAnimation)animationOptions];
|
[super insertRowsAtIndexPaths:indexPaths withRowAnimation:(UITableViewRowAnimation)animationOptions];
|
||||||
// Push this to the next runloop to be sure the UITableView has the right content size
|
[self _scheduleCheckForBatchFetching];
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
[self _checkForBatchFetching];
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (_automaticallyAdjustsContentOffset) {
|
if (_automaticallyAdjustsContentOffset) {
|
||||||
@ -923,10 +931,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone;
|
BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone;
|
||||||
ASPerformBlockWithoutAnimation(preventAnimation, ^{
|
ASPerformBlockWithoutAnimation(preventAnimation, ^{
|
||||||
[super deleteRowsAtIndexPaths:indexPaths withRowAnimation:(UITableViewRowAnimation)animationOptions];
|
[super deleteRowsAtIndexPaths:indexPaths withRowAnimation:(UITableViewRowAnimation)animationOptions];
|
||||||
// Push this to the next runloop to be sure the UITableView has the right content size
|
[self _scheduleCheckForBatchFetching];
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
[self _checkForBatchFetching];
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (_automaticallyAdjustsContentOffset) {
|
if (_automaticallyAdjustsContentOffset) {
|
||||||
@ -947,10 +952,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone;
|
BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone;
|
||||||
ASPerformBlockWithoutAnimation(preventAnimation, ^{
|
ASPerformBlockWithoutAnimation(preventAnimation, ^{
|
||||||
[super insertSections:indexSet withRowAnimation:(UITableViewRowAnimation)animationOptions];
|
[super insertSections:indexSet withRowAnimation:(UITableViewRowAnimation)animationOptions];
|
||||||
// Push this to the next runloop to be sure the UITableView has the right content size
|
[self _scheduleCheckForBatchFetching];
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
[self _checkForBatchFetching];
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -966,10 +968,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone;
|
BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone;
|
||||||
ASPerformBlockWithoutAnimation(preventAnimation, ^{
|
ASPerformBlockWithoutAnimation(preventAnimation, ^{
|
||||||
[super deleteSections:indexSet withRowAnimation:(UITableViewRowAnimation)animationOptions];
|
[super deleteSections:indexSet withRowAnimation:(UITableViewRowAnimation)animationOptions];
|
||||||
// Push this to the next runloop to be sure the UITableView has the right content size
|
[self _scheduleCheckForBatchFetching];
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
[self _checkForBatchFetching];
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,27 +10,26 @@
|
|||||||
|
|
||||||
#import "ASBatchContext.h"
|
#import "ASBatchContext.h"
|
||||||
#import "ASScrollDirection.h"
|
#import "ASScrollDirection.h"
|
||||||
#import "ASBaseDefines.h"
|
|
||||||
|
|
||||||
ASDISPLAYNODE_EXTERN_C_BEGIN
|
ASDISPLAYNODE_EXTERN_C_BEGIN
|
||||||
|
|
||||||
|
@protocol ASBatchFetchingScrollView <NSObject>
|
||||||
|
|
||||||
|
- (BOOL)canBatchFetch;
|
||||||
|
- (ASBatchContext *)batchContext;
|
||||||
|
- (CGFloat)leadingScreensForBatching;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@abstract Determine if batch fetching should begin based on the state of the parameters.
|
@abstract Determine if batch fetching should begin based on the state of the parameters.
|
||||||
@param context The batch fetching context that contains knowledge about in-flight fetches.
|
|
||||||
@param scrollDirection The current scrolling direction of the scroll view.
|
|
||||||
@param bounds The bounds of the scrollview.
|
|
||||||
@param contentSize The content size of the scrollview.
|
|
||||||
@param targetOffset The offset that the scrollview will scroll to.
|
|
||||||
@param leadingScreens How many screens in the remaining distance will trigger batch fetching.
|
|
||||||
@return Whether or not the current state should proceed with batch fetching.
|
|
||||||
@discussion This method is broken into a category for unit testing purposes and should be used with the ASTableView and
|
@discussion This method is broken into a category for unit testing purposes and should be used with the ASTableView and
|
||||||
* ASCollectionView batch fetching API.
|
* ASCollectionView batch fetching API.
|
||||||
|
@param context The scroll view that in-flight fetches are happening.
|
||||||
|
@param scrollDirection The current scrolling direction of the scroll view.
|
||||||
|
@param targetOffset The offset that the scrollview will scroll to.
|
||||||
|
@return Whether or not the current state should proceed with batch fetching.
|
||||||
*/
|
*/
|
||||||
extern BOOL ASDisplayShouldFetchBatchForContext(ASBatchContext *context,
|
BOOL ASDisplayShouldFetchBatchForScrollView(UIScrollView<ASBatchFetchingScrollView> *scrollView, ASScrollDirection scrollDirection, CGPoint contentOffset);
|
||||||
ASScrollDirection scrollDirection,
|
|
||||||
CGRect bounds,
|
|
||||||
CGSize contentSize,
|
|
||||||
CGPoint targetOffset,
|
|
||||||
CGFloat leadingScreens);
|
|
||||||
|
|
||||||
ASDISPLAYNODE_EXTERN_C_END
|
ASDISPLAYNODE_EXTERN_C_END
|
||||||
|
|||||||
@ -8,23 +8,31 @@
|
|||||||
|
|
||||||
#import "ASBatchFetching.h"
|
#import "ASBatchFetching.h"
|
||||||
|
|
||||||
BOOL ASDisplayShouldFetchBatchForContext(ASBatchContext *context,
|
BOOL ASDisplayShouldFetchBatchForScrollView(UIScrollView<ASBatchFetchingScrollView> *scrollView, ASScrollDirection scrollDirection, CGPoint contentOffset)
|
||||||
ASScrollDirection scrollDirection,
|
{
|
||||||
CGRect bounds,
|
|
||||||
CGSize contentSize,
|
// Don't fetch if the scroll view does not allow
|
||||||
CGPoint targetOffset,
|
if (![scrollView canBatchFetch]) {
|
||||||
CGFloat leadingScreens) {
|
return NO;
|
||||||
// do not allow fetching if a batch is already in-flight and hasn't been completed or cancelled
|
}
|
||||||
|
|
||||||
|
// Check if we should batch fetch
|
||||||
|
ASBatchContext *context = scrollView.batchContext;
|
||||||
|
CGRect bounds = scrollView.bounds;
|
||||||
|
CGSize contentSize = scrollView.contentSize;
|
||||||
|
CGFloat leadingScreens = scrollView.leadingScreensForBatching;
|
||||||
|
|
||||||
|
// Do not allow fetching if a batch is already in-flight and hasn't been completed or cancelled
|
||||||
if ([context isFetching]) {
|
if ([context isFetching]) {
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
// only Down and Right scrolls are currently supported (tail loading)
|
// Only Down and Right scrolls are currently supported (tail loading)
|
||||||
if (!ASScrollDirectionContainsDown(scrollDirection) && !ASScrollDirectionContainsRight(scrollDirection)) {
|
if (!ASScrollDirectionContainsDown(scrollDirection) && !ASScrollDirectionContainsRight(scrollDirection)) {
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
// no fetching for null states
|
// No fetching for null states
|
||||||
if (leadingScreens <= 0.0 || CGRectEqualToRect(bounds, CGRectZero)) {
|
if (leadingScreens <= 0.0 || CGRectEqualToRect(bounds, CGRectZero)) {
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
@ -33,11 +41,11 @@ BOOL ASDisplayShouldFetchBatchForContext(ASBatchContext *context,
|
|||||||
|
|
||||||
if (ASScrollDirectionContainsDown(scrollDirection)) {
|
if (ASScrollDirectionContainsDown(scrollDirection)) {
|
||||||
viewLength = bounds.size.height;
|
viewLength = bounds.size.height;
|
||||||
offset = targetOffset.y;
|
offset = contentOffset.y;
|
||||||
contentLength = contentSize.height;
|
contentLength = contentSize.height;
|
||||||
} else { // horizontal / right
|
} else { // horizontal / right
|
||||||
viewLength = bounds.size.width;
|
viewLength = bounds.size.width;
|
||||||
offset = targetOffset.x;
|
offset = contentOffset.x;
|
||||||
contentLength = contentSize.width;
|
contentLength = contentSize.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user