mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-11 00:40:25 +00:00
Ensure that ASRangeController immediately removes any deleted nodes from its range state.
This commit is contained in:
parent
9b9d8bc9b6
commit
ffcddf36e2
@ -927,4 +927,23 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - UICollectionView dead-end intercepts
|
||||||
|
|
||||||
|
#if ASDISPLAYNODE_ASSERTIONS_ENABLED // Remove implementations entirely for efficiency if not asserting.
|
||||||
|
|
||||||
|
// intercepted due to not being supported by ASCollectionView (prevent bugs caused by usage)
|
||||||
|
|
||||||
|
- (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(9_0)
|
||||||
|
{
|
||||||
|
ASDisplayNodeAssert(![self.asyncDataSource respondsToSelector:_cmd], @"%@ is not supported by ASCollectionView - please remove or disable this data source method.", NSStringFromSelector(_cmd));
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath*)destinationIndexPath NS_AVAILABLE_IOS(9_0)
|
||||||
|
{
|
||||||
|
ASDisplayNodeAssert(![self.asyncDataSource respondsToSelector:_cmd], @"%@ is not supported by ASCollectionView - please remove or disable this data source method.", NSStringFromSelector(_cmd));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@ -17,27 +17,24 @@
|
|||||||
- (ASCellNode *)pagerNode:(ASPagerNode *)pagerNode nodeAtIndex:(NSInteger)index;
|
- (ASCellNode *)pagerNode:(ASPagerNode *)pagerNode nodeAtIndex:(NSInteger)index;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
// WARNING: ASPagerNode is new in AsyncDisplayKit 1.9.4 and not yet widely tested.
|
|
||||||
// Details of its API or behavior may change in future releases
|
|
||||||
@interface ASPagerNode : ASCollectionNode
|
@interface ASPagerNode : ASCollectionNode
|
||||||
|
|
||||||
// Configures a default horizontal, paging flow layout with 0 inter-item spacing.
|
// Configures a default horizontal, paging flow layout with 0 inter-item spacing.
|
||||||
- (instancetype)init;
|
- (instancetype)init;
|
||||||
|
|
||||||
// Initializer with custom-configured flow layout properties.
|
// Initializer with custom-configured flow layout properties.
|
||||||
- (instancetype)initWithFlowLayout:(UICollectionViewFlowLayout *)flowLayout;
|
- (instancetype)initWithCollectionViewLayout:(UICollectionViewFlowLayout *)flowLayout;
|
||||||
|
|
||||||
// The underlying ASCollectionView object.
|
// Data Source is required, and uses a different protocol from ASCollectionNode.
|
||||||
@property (nonatomic, readonly) ASCollectionView *view;
|
- (void)setDataSource:(id <ASPagerNodeDataSource>)dataSource;
|
||||||
|
- (id <ASPagerNodeDataSource>)dataSource;
|
||||||
|
|
||||||
// Delegate is optional, and uses the same protocol as ASCollectionNode.
|
// Delegate is optional, and uses the same protocol as ASCollectionNode.
|
||||||
// This includes UIScrollViewDelegate as well as most methods from UICollectionViewDelegate, like willDisplay...
|
// This includes UIScrollViewDelegate as well as most methods from UICollectionViewDelegate, like willDisplay...
|
||||||
@property (nonatomic, weak) id <ASCollectionDelegate> delegate;
|
@property (nonatomic, weak) id <ASCollectionDelegate> delegate;
|
||||||
|
|
||||||
// Data Source is required, and uses a different protocol from ASCollectionNode.
|
// The underlying ASCollectionView object.
|
||||||
//@property (nonatomic, weak) id <ASPagerNodeDataSource> dataSource;
|
@property (nonatomic, readonly) ASCollectionView *view;
|
||||||
- (void)setDataSource:(id <ASPagerNodeDataSource>)dataSource;
|
|
||||||
- (id <ASPagerNodeDataSource>)dataSource;
|
|
||||||
|
|
||||||
- (void)scrollToPageAtIndex:(NSInteger)index animated:(BOOL)animated;
|
- (void)scrollToPageAtIndex:(NSInteger)index animated:(BOOL)animated;
|
||||||
|
|
||||||
|
|||||||
@ -29,17 +29,12 @@
|
|||||||
flowLayout.minimumInteritemSpacing = 0;
|
flowLayout.minimumInteritemSpacing = 0;
|
||||||
flowLayout.minimumLineSpacing = 0;
|
flowLayout.minimumLineSpacing = 0;
|
||||||
|
|
||||||
return [self initWithFlowLayout:flowLayout];
|
return [self initWithCollectionViewLayout:flowLayout];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout
|
- (instancetype)initWithCollectionViewLayout:(UICollectionViewFlowLayout *)flowLayout;
|
||||||
{
|
|
||||||
ASDisplayNodeAssert([layout isKindOfClass:[UICollectionViewFlowLayout class]], @"ASPagerNode requires a flow layout.");
|
|
||||||
return [self initWithFlowLayout:(UICollectionViewFlowLayout *)layout];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (instancetype)initWithFlowLayout:(UICollectionViewFlowLayout *)flowLayout
|
|
||||||
{
|
{
|
||||||
|
ASDisplayNodeAssert([flowLayout isKindOfClass:[UICollectionViewFlowLayout class]], @"ASPagerNode requires a flow layout.");
|
||||||
self = [super initWithCollectionViewLayout:flowLayout];
|
self = [super initWithCollectionViewLayout:flowLayout];
|
||||||
if (self != nil) {
|
if (self != nil) {
|
||||||
_flowLayout = flowLayout;
|
_flowLayout = flowLayout;
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
@class ASDelegateProxy;
|
@class ASDelegateProxy;
|
||||||
@protocol ASDelegateProxyInterceptor
|
@protocol ASDelegateProxyInterceptor <NSObject>
|
||||||
@required
|
@required
|
||||||
// Called if the target object is discovered to be nil if it had been non-nil at init time.
|
// Called if the target object is discovered to be nil if it had been non-nil at init time.
|
||||||
// This happens if the object is deallocated, because the proxy must maintain a weak reference to avoid cycles.
|
// This happens if the object is deallocated, because the proxy must maintain a weak reference to avoid cycles.
|
||||||
@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
@interface ASDelegateProxy : NSProxy
|
@interface ASDelegateProxy : NSProxy
|
||||||
|
|
||||||
- (instancetype)initWithTarget:(id<NSObject>)target interceptor:(id <ASDelegateProxyInterceptor>)interceptor;
|
- (instancetype)initWithTarget:(id <NSObject>)target interceptor:(id <ASDelegateProxyInterceptor>)interceptor;
|
||||||
|
|
||||||
// This method must be overridden by a subclass.
|
// This method must be overridden by a subclass.
|
||||||
- (BOOL)interceptsSelector:(SEL)selector;
|
- (BOOL)interceptsSelector:(SEL)selector;
|
||||||
|
|||||||
@ -54,7 +54,11 @@
|
|||||||
selector == @selector(collectionView:didEndDisplayingCell:forItemAtIndexPath:) ||
|
selector == @selector(collectionView:didEndDisplayingCell:forItemAtIndexPath:) ||
|
||||||
|
|
||||||
// used for batch fetching API
|
// used for batch fetching API
|
||||||
selector == @selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)
|
selector == @selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:) ||
|
||||||
|
|
||||||
|
// intercepted due to not being supported by ASCollectionView (prevent bugs caused by usage)
|
||||||
|
selector == @selector(collectionView:canMoveItemAtIndexPath:) ||
|
||||||
|
selector == @selector(collectionView:moveItemAtIndexPath:toIndexPath:)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,7 +101,7 @@
|
|||||||
- (BOOL)respondsToSelector:(SEL)aSelector
|
- (BOOL)respondsToSelector:(SEL)aSelector
|
||||||
{
|
{
|
||||||
if ([self interceptsSelector:aSelector]) {
|
if ([self interceptsSelector:aSelector]) {
|
||||||
return (_interceptor != nil);
|
return [_interceptor respondsToSelector:aSelector];
|
||||||
} else {
|
} else {
|
||||||
// Also return NO if _target has become nil due to zeroing weak reference (or placeholder initialization).
|
// Also return NO if _target has become nil due to zeroing weak reference (or placeholder initialization).
|
||||||
return [_target respondsToSelector:aSelector];
|
return [_target respondsToSelector:aSelector];
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
typedef NS_ENUM(NSInteger, ASLayoutRangeType) {
|
typedef NS_ENUM(NSInteger, ASLayoutRangeType) {
|
||||||
ASLayoutRangeTypeVisible,
|
ASLayoutRangeTypeVisible = 0,
|
||||||
ASLayoutRangeTypeRender,
|
ASLayoutRangeTypeRender,
|
||||||
ASLayoutRangeTypePreload,
|
ASLayoutRangeTypePreload,
|
||||||
ASLayoutRangeTypeCount
|
ASLayoutRangeTypeCount
|
||||||
|
|||||||
@ -130,7 +130,8 @@
|
|||||||
rangeType:rangeType];
|
rangeType:rangeType];
|
||||||
|
|
||||||
// Notify to remove indexpaths that are leftover that are not visible or included in the _layoutController calculated paths
|
// Notify to remove indexpaths that are leftover that are not visible or included in the _layoutController calculated paths
|
||||||
NSMutableSet *removedIndexPaths = _rangeIsValid ? [_rangeTypeIndexPaths[rangeKey] mutableCopy] : [NSMutableSet set];
|
// This value may be nil for the first call of this method.
|
||||||
|
NSMutableSet *removedIndexPaths = [_rangeTypeIndexPaths[rangeKey] mutableCopy];
|
||||||
[removedIndexPaths minusSet:indexPaths];
|
[removedIndexPaths minusSet:indexPaths];
|
||||||
[removedIndexPaths minusSet:visibleNodePathsSet];
|
[removedIndexPaths minusSet:visibleNodePathsSet];
|
||||||
|
|
||||||
@ -176,61 +177,76 @@
|
|||||||
|
|
||||||
#pragma mark - ASDataControllerDelegete
|
#pragma mark - ASDataControllerDelegete
|
||||||
|
|
||||||
- (void)dataControllerBeginUpdates:(ASDataController *)dataController {
|
- (void)dataControllerBeginUpdates:(ASDataController *)dataController
|
||||||
|
{
|
||||||
ASPerformBlockOnMainThread(^{
|
ASPerformBlockOnMainThread(^{
|
||||||
[_delegate didBeginUpdatesInRangeController:self];
|
[_delegate didBeginUpdatesInRangeController:self];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dataController:(ASDataController *)dataController endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion {
|
- (void)dataController:(ASDataController *)dataController endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion
|
||||||
|
{
|
||||||
ASPerformBlockOnMainThread(^{
|
ASPerformBlockOnMainThread(^{
|
||||||
[_delegate rangeController:self didEndUpdatesAnimated:animated completion:completion];
|
[_delegate rangeController:self didEndUpdatesAnimated:animated completion:completion];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dataController:(ASDataController *)dataController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions {
|
- (void)dataController:(ASDataController *)dataController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
|
{
|
||||||
ASDisplayNodeAssert(nodes.count == indexPaths.count, @"Invalid index path");
|
ASDisplayNodeAssert(nodes.count == indexPaths.count, @"Invalid index path");
|
||||||
|
|
||||||
NSMutableArray *nodeSizes = [NSMutableArray arrayWithCapacity:nodes.count];
|
|
||||||
[nodes enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger idx, BOOL *stop) {
|
|
||||||
[nodeSizes addObject:[NSValue valueWithCGSize:node.calculatedSize]];
|
|
||||||
}];
|
|
||||||
|
|
||||||
ASPerformBlockOnMainThread(^{
|
ASPerformBlockOnMainThread(^{
|
||||||
_rangeIsValid = NO;
|
_rangeIsValid = NO;
|
||||||
[_delegate rangeController:self didInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
[_delegate rangeController:self didInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dataController:(ASDataController *)dataController didDeleteNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions {
|
- (void)dataController:(ASDataController *)dataController didDeleteNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
|
{
|
||||||
ASPerformBlockOnMainThread(^{
|
ASPerformBlockOnMainThread(^{
|
||||||
_rangeIsValid = NO;
|
_rangeIsValid = NO;
|
||||||
|
|
||||||
|
// When removing nodes we need to make sure that removed indexPaths are not left in _rangeTypeIndexPaths,
|
||||||
|
// otherwise _updateVisibleNodeIndexPaths may try to retrieve nodes from dataSource that aren't there anymore
|
||||||
|
for (NSInteger i = 0; i < ASLayoutRangeTypeCount; i++) {
|
||||||
|
id rangeKey = @((ASLayoutRangeType)i);
|
||||||
|
NSMutableSet *rangePaths = [_rangeTypeIndexPaths[rangeKey] mutableCopy];
|
||||||
|
for (NSIndexPath *path in indexPaths) {
|
||||||
|
[rangePaths removeObject:path];
|
||||||
|
}
|
||||||
|
_rangeTypeIndexPaths[rangeKey] = rangePaths;
|
||||||
|
}
|
||||||
|
|
||||||
[_delegate rangeController:self didDeleteNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
[_delegate rangeController:self didDeleteNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dataController:(ASDataController *)dataController didInsertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions {
|
- (void)dataController:(ASDataController *)dataController didInsertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
|
{
|
||||||
ASDisplayNodeAssert(sections.count == indexSet.count, @"Invalid sections");
|
ASDisplayNodeAssert(sections.count == indexSet.count, @"Invalid sections");
|
||||||
|
|
||||||
NSMutableArray *sectionNodeSizes = [NSMutableArray arrayWithCapacity:sections.count];
|
|
||||||
|
|
||||||
[sections enumerateObjectsUsingBlock:^(NSArray *nodes, NSUInteger idx, BOOL *stop) {
|
|
||||||
NSMutableArray *nodeSizes = [NSMutableArray arrayWithCapacity:nodes.count];
|
|
||||||
[nodes enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger idx2, BOOL *stop2) {
|
|
||||||
[nodeSizes addObject:[NSValue valueWithCGSize:node.calculatedSize]];
|
|
||||||
}];
|
|
||||||
[sectionNodeSizes addObject:nodeSizes];
|
|
||||||
}];
|
|
||||||
|
|
||||||
ASPerformBlockOnMainThread(^{
|
ASPerformBlockOnMainThread(^{
|
||||||
_rangeIsValid = NO;
|
_rangeIsValid = NO;
|
||||||
[_delegate rangeController:self didInsertSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
|
[_delegate rangeController:self didInsertSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dataController:(ASDataController *)dataController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions {
|
- (void)dataController:(ASDataController *)dataController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
|
{
|
||||||
ASPerformBlockOnMainThread(^{
|
ASPerformBlockOnMainThread(^{
|
||||||
_rangeIsValid = NO;
|
_rangeIsValid = NO;
|
||||||
|
|
||||||
|
// When removing nodes we need to make sure that removed indexPaths are not left in _rangeTypeIndexPaths,
|
||||||
|
// otherwise _updateVisibleNodeIndexPaths may try to retrieve nodes from dataSource that aren't there anymore
|
||||||
|
for (NSInteger i = 0; i < ASLayoutRangeTypeCount; i++) {
|
||||||
|
id rangeKey = @((ASLayoutRangeType)i);
|
||||||
|
NSMutableSet *rangePaths = [_rangeTypeIndexPaths[rangeKey] mutableCopy];
|
||||||
|
for (NSIndexPath *path in _rangeTypeIndexPaths[rangeKey]) {
|
||||||
|
if ([indexSet containsIndex:path.section]) {
|
||||||
|
[rangePaths removeObject:path];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_rangeTypeIndexPaths[rangeKey] = rangePaths;
|
||||||
|
}
|
||||||
|
|
||||||
[_delegate rangeController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
|
[_delegate rangeController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,4 +19,6 @@
|
|||||||
|
|
||||||
- (instancetype)initWithElementSize:(CGSize)size;
|
- (instancetype)initWithElementSize:(CGSize)size;
|
||||||
|
|
||||||
|
@property (nonatomic) NSInteger pageNumber;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@ -63,9 +63,16 @@
|
|||||||
{
|
{
|
||||||
RandomCoreGraphicsNode *elementNode = [[RandomCoreGraphicsNode alloc] init];
|
RandomCoreGraphicsNode *elementNode = [[RandomCoreGraphicsNode alloc] init];
|
||||||
elementNode.preferredFrameSize = _elementSize;
|
elementNode.preferredFrameSize = _elementSize;
|
||||||
|
elementNode.indexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:_pageNumber];
|
||||||
return elementNode;
|
return elementNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
||||||
|
{
|
||||||
|
[tableView deselectRowAtIndexPath:indexPath animated:NO];
|
||||||
|
[_tableNode.view reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)layout
|
- (void)layout
|
||||||
{
|
{
|
||||||
[super layout];
|
[super layout];
|
||||||
|
|||||||
@ -10,4 +10,6 @@
|
|||||||
|
|
||||||
@interface RandomCoreGraphicsNode : ASCellNode
|
@interface RandomCoreGraphicsNode : ASCellNode
|
||||||
|
|
||||||
|
@property (nonatomic) NSIndexPath *indexPath;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@ -42,4 +42,24 @@
|
|||||||
CGColorSpaceRelease(colorSpace);
|
CGColorSpaceRelease(colorSpace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
- (void)fetchData
|
||||||
|
{
|
||||||
|
NSLog(@"fetchData - %@, %@", self, self.indexPath);
|
||||||
|
[super fetchData];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)clearFetchedData
|
||||||
|
{
|
||||||
|
NSLog(@"clearFetchedData - %@, %@", self, self.indexPath);
|
||||||
|
[super clearFetchedData];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)visibilityDidChange:(BOOL)isVisible
|
||||||
|
{
|
||||||
|
NSLog(@"visibilityDidChange:%d - %@, %@", isVisible, self, self.indexPath);
|
||||||
|
[super visibilityDidChange:isVisible];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@ -69,12 +69,13 @@
|
|||||||
#pragma mark -
|
#pragma mark -
|
||||||
#pragma mark ASPagerNode.
|
#pragma mark ASPagerNode.
|
||||||
|
|
||||||
- (ASCellNode *)pagerNode:(ASPagerNode *)pagerNode nodeAtIndex:(NSInteger)index;
|
- (ASCellNode *)pagerNode:(ASPagerNode *)pagerNode nodeAtIndex:(NSInteger)index
|
||||||
{
|
{
|
||||||
CGSize boundsSize = pagerNode.bounds.size;
|
CGSize boundsSize = pagerNode.bounds.size;
|
||||||
CGSize gradientRowSize = CGSizeMake(boundsSize.width, 100);
|
CGSize gradientRowSize = CGSizeMake(boundsSize.width, 100);
|
||||||
GradientTableNode *node = [[GradientTableNode alloc] initWithElementSize:gradientRowSize];
|
GradientTableNode *node = [[GradientTableNode alloc] initWithElementSize:gradientRowSize];
|
||||||
node.preferredFrameSize = boundsSize;
|
node.preferredFrameSize = boundsSize;
|
||||||
|
node.pageNumber = index;
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user