diff --git a/AsyncDisplayKit/ASCellNode+Internal.h b/AsyncDisplayKit/ASCellNode+Internal.h new file mode 100644 index 0000000000..a660e46d80 --- /dev/null +++ b/AsyncDisplayKit/ASCellNode+Internal.h @@ -0,0 +1,13 @@ +// +// ASCellNode+Internal.h +// Pods +// +// Created by Max Gu on 2/19/16. +// +// + +#import "ASCellNode.h" + +@interface ASCellNode (Internal) + +@end diff --git a/AsyncDisplayKit/ASCellNode.h b/AsyncDisplayKit/ASCellNode.h index d1e575383e..8788d30015 100644 --- a/AsyncDisplayKit/ASCellNode.h +++ b/AsyncDisplayKit/ASCellNode.h @@ -24,6 +24,7 @@ typedef NSUInteger ASCellNodeAnimation; * @param sizeChanged `YES` if the node's `calculatedSize` changed during the relayout, `NO` otherwise. */ - (void)nodeDidRelayout:(ASCellNode *)node sizeChanged:(BOOL)sizeChanged; + @end /** @@ -107,6 +108,8 @@ typedef NSUInteger ASCellNodeAnimation; */ - (instancetype)initWithViewControllerBlock:(ASDisplayNodeViewControllerBlock)viewControllerBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock; +- (void)visibleNodeDidScroll:(UIScrollView *)scrollView withCellFrame:(CGRect)cellFrame; + @end diff --git a/AsyncDisplayKit/ASCellNode.m b/AsyncDisplayKit/ASCellNode.m index df7040d92e..3e1fccdbc8 100644 --- a/AsyncDisplayKit/ASCellNode.m +++ b/AsyncDisplayKit/ASCellNode.m @@ -36,7 +36,6 @@ // Use UITableViewCell defaults _selectionStyle = UITableViewCellSelectionStyleDefault; self.clipsToBounds = YES; - return self; } @@ -56,7 +55,7 @@ [self addSubnode:_viewControllerNode]; } - + return self; } @@ -134,6 +133,11 @@ [(_ASDisplayView *)self.view __forwardTouchesCancelled:touches withEvent:event]; } +- (void)visibleNodeDidScroll:(UIScrollView *)scrollView withCellFrame:(CGRect)cellFrame +{ + // To be overriden by subclasses +} + @end diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 07484f3622..20e30b7c40 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -9,6 +9,7 @@ #import "ASAssert.h" #import "ASBatchFetching.h" #import "ASDelegateProxy.h" +#import "ASCellNode+Internal.h" #import "ASCollectionNode.h" #import "ASCollectionDataController.h" #import "ASCollectionViewLayoutController.h" @@ -67,7 +68,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; ASRangeController *_rangeController; ASCollectionViewLayoutController *_layoutController; ASCollectionViewFlowLayoutInspector *_flowLayoutInspector; - + NSMutableSet *_cellsForVisibilityUpdates; id _layoutFacilitator; BOOL _performingBatchUpdates; @@ -75,6 +76,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; BOOL _asyncDataFetchingEnabled; BOOL _asyncDelegateImplementsInsetSection; + BOOL _asyncDelegateImplementsScrollviewDidScroll; BOOL _collectionViewLayoutImplementsInsetSection; BOOL _asyncDataSourceImplementsConstrainedSizeForNode; BOOL _asyncDataSourceImplementsNodeBlockForItemAtIndexPath; @@ -212,6 +214,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; _registeredSupplementaryKinds = [NSMutableSet set]; + _cellsForVisibilityUpdates = [NSMutableSet set]; self.backgroundColor = [UIColor whiteColor]; [self registerClass:[_ASCollectionViewCell class] forCellWithReuseIdentifier:kCellReuseIdentifier]; @@ -328,12 +331,14 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; _asyncDelegate = nil; _proxyDelegate = _isDeallocating ? nil : [[ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self]; _asyncDelegateImplementsInsetSection = NO; + _asyncDelegateImplementsScrollviewDidScroll = NO; } else { _asyncDelegate = asyncDelegate; _proxyDelegate = [[ASCollectionViewProxy alloc] initWithTarget:_asyncDelegate interceptor:self]; _asyncDelegateImplementsInsetSection = ([_asyncDelegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)] ? 1 : 0); + _asyncDelegateImplementsScrollviewDidScroll = ([_asyncDelegate respondsToSelector:@selector(scrollViewDidScroll:)] ? 1 : 0); } - + super.delegate = (id)_proxyDelegate; [_layoutInspector didChangeCollectionViewDelegate:asyncDelegate]; @@ -525,7 +530,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; return cell; } -- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath +- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(_ASCollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath { [_rangeController visibleNodeIndexPathsDidChangeWithScrollDirection:[self scrollDirection]]; @@ -533,10 +538,13 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; [_asyncDelegate collectionView:self willDisplayNodeForItemAtIndexPath:indexPath]; } - ASCellNode *cellNode = [self nodeForItemAtIndexPath:indexPath]; + ASCellNode *cellNode = [cell node]; if (cellNode.neverShowPlaceholders) { [cellNode recursivelyEnsureDisplaySynchronously:YES]; } + if (ASSubclassOverridesSelector([ASCellNode class], [cellNode class], @selector(visibleNodeDidScroll:withCellFrame:))) { + [_cellsForVisibilityUpdates addObject:cell]; + } } - (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath @@ -548,6 +556,9 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; ASDisplayNodeAssertNotNil(node, @"Expected node associated with removed cell not to be nil."); [_asyncDelegate collectionView:self didEndDisplayingNode:node forItemAtIndexPath:indexPath]; } + [_cellsForVisibilityUpdates removeObject:cell]; + + #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" if ([_asyncDelegate respondsToSelector:@selector(collectionView:didEndDisplayingNodeForItemAtIndexPath:)]) { @@ -664,6 +675,18 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; } } +- (void)scrollViewDidScroll:(UIScrollView *)scrollView +{ + for (_ASCollectionViewCell *collectionCell in _cellsForVisibilityUpdates) { + ASCellNode *node = [collectionCell node]; + // Only nodes that respond to the selector are added to _cellsForVisibilityUpdates + [node visibleNodeDidScroll: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 diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 5ef50f7d88..c2e7011e62 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -112,6 +112,8 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; BOOL _queuedNodeHeightUpdate; BOOL _isDeallocating; BOOL _dataSourceImplementsNodeBlockForRowAtIndexPath; + BOOL _asyncDelegateImplementsScrollviewDidScroll; + NSMutableSet *_cellsForVisibilityUpdates; } @property (atomic, assign) BOOL asyncDataSourceLocked; @@ -197,7 +199,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; if (!(self = [super initWithFrame:frame style:style])) { return nil; } - + _cellsForVisibilityUpdates = [NSMutableSet set]; if (!dataControllerClass) { dataControllerClass = [[self class] dataControllerClass]; } @@ -585,7 +587,18 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; return direction; } -- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath +- (void)scrollViewDidScroll:(UIScrollView *)scrollView +{ + for (_ASTableViewCell *tableCell in _cellsForVisibilityUpdates) { + ASCellNode *node = [tableCell node]; + [node visibleNodeDidScroll:scrollView withCellFrame:tableCell.frame]; + } + if (_asyncDelegateImplementsScrollviewDidScroll) { + [_asyncDelegate scrollViewDidScroll:scrollView]; + } +} + +- (void)tableView:(UITableView *)tableView willDisplayCell:(_ASTableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { _pendingVisibleIndexPath = indexPath; @@ -595,13 +608,17 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; [_asyncDelegate tableView:self willDisplayNodeForRowAtIndexPath:indexPath]; } - ASCellNode *cellNode = [self nodeForRowAtIndexPath:indexPath]; + ASCellNode *cellNode = [cell node]; + + if (ASSubclassOverridesSelector([ASCellNode class], [cellNode class], @selector(visibleNodeDidScroll:withCellFrame:))) { + [_cellsForVisibilityUpdates addObject:cell]; + } if (cellNode.neverShowPlaceholders) { [cellNode recursivelyEnsureDisplaySynchronously:YES]; } } -- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath +- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(_ASTableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath { if ([_pendingVisibleIndexPath isEqual:indexPath]) { _pendingVisibleIndexPath = nil; @@ -615,6 +632,8 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; [_asyncDelegate tableView:self didEndDisplayingNode:node forRowAtIndexPath:indexPath]; } + [_cellsForVisibilityUpdates removeObject:cell]; + #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" if ([_asyncDelegate respondsToSelector:@selector(tableView:didEndDisplayingNodeForRowAtIndexPath:)]) { diff --git a/AsyncDisplayKit/Details/ASDelegateProxy.m b/AsyncDisplayKit/Details/ASDelegateProxy.m index 2af0e7c7d5..3034a83d86 100644 --- a/AsyncDisplayKit/Details/ASDelegateProxy.m +++ b/AsyncDisplayKit/Details/ASDelegateProxy.m @@ -24,6 +24,9 @@ selector == @selector(numberOfSectionsInTableView:) || selector == @selector(tableView:numberOfRowsInSection:) || + // used for ASCellNode visibility + selector == @selector(scrollViewDidScroll:) || + // used for ASRangeController visibility updates selector == @selector(tableView:willDisplayCell:forRowAtIndexPath:) || selector == @selector(tableView:didEndDisplayingCell:forRowAtIndexPath:) || @@ -56,6 +59,9 @@ // used for batch fetching API selector == @selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:) || + // used for ASCellNode visibility + selector == @selector(scrollViewDidScroll:) || + // intercepted due to not being supported by ASCollectionView (prevent bugs caused by usage) selector == @selector(collectionView:canMoveItemAtIndexPath:) || selector == @selector(collectionView:moveItemAtIndexPath:toIndexPath:) ||