Skip to content
This repository was archived by the owner on Feb 2, 2023. It is now read-only.

Commit 5a7772a

Browse files
committed
Merge pull request #1020 from facebook/RangeDeletions
Ensure that ASRangeController immediately removes any deleted nodes from its range state.
2 parents 9b9d8bc + ffcddf3 commit 5a7772a

12 files changed

Lines changed: 110 additions & 47 deletions

File tree

AsyncDisplayKit/ASCollectionView.mm

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -927,4 +927,23 @@ - (void)clearFetchedData
927927
}
928928
}
929929

930+
#pragma mark - UICollectionView dead-end intercepts
931+
932+
#if ASDISPLAYNODE_ASSERTIONS_ENABLED // Remove implementations entirely for efficiency if not asserting.
933+
934+
// intercepted due to not being supported by ASCollectionView (prevent bugs caused by usage)
935+
936+
- (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(9_0)
937+
{
938+
ASDisplayNodeAssert(![self.asyncDataSource respondsToSelector:_cmd], @"%@ is not supported by ASCollectionView - please remove or disable this data source method.", NSStringFromSelector(_cmd));
939+
return NO;
940+
}
941+
942+
- (void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath*)destinationIndexPath NS_AVAILABLE_IOS(9_0)
943+
{
944+
ASDisplayNodeAssert(![self.asyncDataSource respondsToSelector:_cmd], @"%@ is not supported by ASCollectionView - please remove or disable this data source method.", NSStringFromSelector(_cmd));
945+
}
946+
947+
#endif
948+
930949
@end

AsyncDisplayKit/ASPagerNode.h

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,24 @@
1717
- (ASCellNode *)pagerNode:(ASPagerNode *)pagerNode nodeAtIndex:(NSInteger)index;
1818
@end
1919

20-
// WARNING: ASPagerNode is new in AsyncDisplayKit 1.9.4 and not yet widely tested.
21-
// Details of its API or behavior may change in future releases
2220
@interface ASPagerNode : ASCollectionNode
2321

2422
// Configures a default horizontal, paging flow layout with 0 inter-item spacing.
2523
- (instancetype)init;
2624

2725
// Initializer with custom-configured flow layout properties.
28-
- (instancetype)initWithFlowLayout:(UICollectionViewFlowLayout *)flowLayout;
26+
- (instancetype)initWithCollectionViewLayout:(UICollectionViewFlowLayout *)flowLayout;
2927

30-
// The underlying ASCollectionView object.
31-
@property (nonatomic, readonly) ASCollectionView *view;
28+
// Data Source is required, and uses a different protocol from ASCollectionNode.
29+
- (void)setDataSource:(id <ASPagerNodeDataSource>)dataSource;
30+
- (id <ASPagerNodeDataSource>)dataSource;
3231

3332
// Delegate is optional, and uses the same protocol as ASCollectionNode.
3433
// This includes UIScrollViewDelegate as well as most methods from UICollectionViewDelegate, like willDisplay...
3534
@property (nonatomic, weak) id <ASCollectionDelegate> delegate;
3635

37-
// Data Source is required, and uses a different protocol from ASCollectionNode.
38-
//@property (nonatomic, weak) id <ASPagerNodeDataSource> dataSource;
39-
- (void)setDataSource:(id <ASPagerNodeDataSource>)dataSource;
40-
- (id <ASPagerNodeDataSource>)dataSource;
36+
// The underlying ASCollectionView object.
37+
@property (nonatomic, readonly) ASCollectionView *view;
4138

4239
- (void)scrollToPageAtIndex:(NSInteger)index animated:(BOOL)animated;
4340

AsyncDisplayKit/ASPagerNode.m

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,12 @@ - (instancetype)init
2929
flowLayout.minimumInteritemSpacing = 0;
3030
flowLayout.minimumLineSpacing = 0;
3131

32-
return [self initWithFlowLayout:flowLayout];
32+
return [self initWithCollectionViewLayout:flowLayout];
3333
}
3434

35-
- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout
36-
{
37-
ASDisplayNodeAssert([layout isKindOfClass:[UICollectionViewFlowLayout class]], @"ASPagerNode requires a flow layout.");
38-
return [self initWithFlowLayout:(UICollectionViewFlowLayout *)layout];
39-
}
40-
41-
- (instancetype)initWithFlowLayout:(UICollectionViewFlowLayout *)flowLayout
35+
- (instancetype)initWithCollectionViewLayout:(UICollectionViewFlowLayout *)flowLayout;
4236
{
37+
ASDisplayNodeAssert([flowLayout isKindOfClass:[UICollectionViewFlowLayout class]], @"ASPagerNode requires a flow layout.");
4338
self = [super initWithCollectionViewLayout:flowLayout];
4439
if (self != nil) {
4540
_flowLayout = flowLayout;

AsyncDisplayKit/Details/ASDelegateProxy.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
#import <Foundation/Foundation.h>
1010

1111
@class ASDelegateProxy;
12-
@protocol ASDelegateProxyInterceptor
12+
@protocol ASDelegateProxyInterceptor <NSObject>
1313
@required
1414
// Called if the target object is discovered to be nil if it had been non-nil at init time.
1515
// This happens if the object is deallocated, because the proxy must maintain a weak reference to avoid cycles.
@@ -25,7 +25,7 @@
2525

2626
@interface ASDelegateProxy : NSProxy
2727

28-
- (instancetype)initWithTarget:(id<NSObject>)target interceptor:(id <ASDelegateProxyInterceptor>)interceptor;
28+
- (instancetype)initWithTarget:(id <NSObject>)target interceptor:(id <ASDelegateProxyInterceptor>)interceptor;
2929

3030
// This method must be overridden by a subclass.
3131
- (BOOL)interceptsSelector:(SEL)selector;

AsyncDisplayKit/Details/ASDelegateProxy.m

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,11 @@ - (BOOL)interceptsSelector:(SEL)selector
5454
selector == @selector(collectionView:didEndDisplayingCell:forItemAtIndexPath:) ||
5555

5656
// used for batch fetching API
57-
selector == @selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)
57+
selector == @selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:) ||
58+
59+
// intercepted due to not being supported by ASCollectionView (prevent bugs caused by usage)
60+
selector == @selector(collectionView:canMoveItemAtIndexPath:) ||
61+
selector == @selector(collectionView:moveItemAtIndexPath:toIndexPath:)
5862
);
5963
}
6064

@@ -97,7 +101,7 @@ - (instancetype)initWithTarget:(id <NSObject>)target interceptor:(id <ASDelegate
97101
- (BOOL)respondsToSelector:(SEL)aSelector
98102
{
99103
if ([self interceptsSelector:aSelector]) {
100-
return (_interceptor != nil);
104+
return [_interceptor respondsToSelector:aSelector];
101105
} else {
102106
// Also return NO if _target has become nil due to zeroing weak reference (or placeholder initialization).
103107
return [_target respondsToSelector:aSelector];

AsyncDisplayKit/Details/ASLayoutRangeType.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
#import <Foundation/Foundation.h>
1010

1111
typedef NS_ENUM(NSInteger, ASLayoutRangeType) {
12-
ASLayoutRangeTypeVisible,
12+
ASLayoutRangeTypeVisible = 0,
1313
ASLayoutRangeTypeRender,
1414
ASLayoutRangeTypePreload,
1515
ASLayoutRangeTypeCount

AsyncDisplayKit/Details/ASRangeController.mm

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ - (void)_updateVisibleNodeIndexPaths
130130
rangeType:rangeType];
131131

132132
// Notify to remove indexpaths that are leftover that are not visible or included in the _layoutController calculated paths
133-
NSMutableSet *removedIndexPaths = _rangeIsValid ? [_rangeTypeIndexPaths[rangeKey] mutableCopy] : [NSMutableSet set];
133+
// This value may be nil for the first call of this method.
134+
NSMutableSet *removedIndexPaths = [_rangeTypeIndexPaths[rangeKey] mutableCopy];
134135
[removedIndexPaths minusSet:indexPaths];
135136
[removedIndexPaths minusSet:visibleNodePathsSet];
136137

@@ -176,61 +177,76 @@ - (BOOL)shouldSkipVisibleNodesForRangeType:(ASLayoutRangeType)rangeType
176177

177178
#pragma mark - ASDataControllerDelegete
178179

179-
- (void)dataControllerBeginUpdates:(ASDataController *)dataController {
180+
- (void)dataControllerBeginUpdates:(ASDataController *)dataController
181+
{
180182
ASPerformBlockOnMainThread(^{
181183
[_delegate didBeginUpdatesInRangeController:self];
182184
});
183185
}
184186

185-
- (void)dataController:(ASDataController *)dataController endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion {
187+
- (void)dataController:(ASDataController *)dataController endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion
188+
{
186189
ASPerformBlockOnMainThread(^{
187190
[_delegate rangeController:self didEndUpdatesAnimated:animated completion:completion];
188191
});
189192
}
190193

191-
- (void)dataController:(ASDataController *)dataController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions {
194+
- (void)dataController:(ASDataController *)dataController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
195+
{
192196
ASDisplayNodeAssert(nodes.count == indexPaths.count, @"Invalid index path");
193-
194-
NSMutableArray *nodeSizes = [NSMutableArray arrayWithCapacity:nodes.count];
195-
[nodes enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger idx, BOOL *stop) {
196-
[nodeSizes addObject:[NSValue valueWithCGSize:node.calculatedSize]];
197-
}];
198-
199197
ASPerformBlockOnMainThread(^{
200198
_rangeIsValid = NO;
201199
[_delegate rangeController:self didInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
202200
});
203201
}
204202

205-
- (void)dataController:(ASDataController *)dataController didDeleteNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions {
203+
- (void)dataController:(ASDataController *)dataController didDeleteNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
204+
{
206205
ASPerformBlockOnMainThread(^{
207206
_rangeIsValid = NO;
207+
208+
// When removing nodes we need to make sure that removed indexPaths are not left in _rangeTypeIndexPaths,
209+
// otherwise _updateVisibleNodeIndexPaths may try to retrieve nodes from dataSource that aren't there anymore
210+
for (NSInteger i = 0; i < ASLayoutRangeTypeCount; i++) {
211+
id rangeKey = @((ASLayoutRangeType)i);
212+
NSMutableSet *rangePaths = [_rangeTypeIndexPaths[rangeKey] mutableCopy];
213+
for (NSIndexPath *path in indexPaths) {
214+
[rangePaths removeObject:path];
215+
}
216+
_rangeTypeIndexPaths[rangeKey] = rangePaths;
217+
}
218+
208219
[_delegate rangeController:self didDeleteNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
209220
});
210221
}
211222

212-
- (void)dataController:(ASDataController *)dataController didInsertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions {
223+
- (void)dataController:(ASDataController *)dataController didInsertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
224+
{
213225
ASDisplayNodeAssert(sections.count == indexSet.count, @"Invalid sections");
214-
215-
NSMutableArray *sectionNodeSizes = [NSMutableArray arrayWithCapacity:sections.count];
216-
217-
[sections enumerateObjectsUsingBlock:^(NSArray *nodes, NSUInteger idx, BOOL *stop) {
218-
NSMutableArray *nodeSizes = [NSMutableArray arrayWithCapacity:nodes.count];
219-
[nodes enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger idx2, BOOL *stop2) {
220-
[nodeSizes addObject:[NSValue valueWithCGSize:node.calculatedSize]];
221-
}];
222-
[sectionNodeSizes addObject:nodeSizes];
223-
}];
224-
225226
ASPerformBlockOnMainThread(^{
226227
_rangeIsValid = NO;
227228
[_delegate rangeController:self didInsertSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
228229
});
229230
}
230231

231-
- (void)dataController:(ASDataController *)dataController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions {
232+
- (void)dataController:(ASDataController *)dataController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
233+
{
232234
ASPerformBlockOnMainThread(^{
233235
_rangeIsValid = NO;
236+
237+
// When removing nodes we need to make sure that removed indexPaths are not left in _rangeTypeIndexPaths,
238+
// otherwise _updateVisibleNodeIndexPaths may try to retrieve nodes from dataSource that aren't there anymore
239+
for (NSInteger i = 0; i < ASLayoutRangeTypeCount; i++) {
240+
id rangeKey = @((ASLayoutRangeType)i);
241+
NSMutableSet *rangePaths = [_rangeTypeIndexPaths[rangeKey] mutableCopy];
242+
for (NSIndexPath *path in _rangeTypeIndexPaths[rangeKey]) {
243+
if ([indexSet containsIndex:path.section]) {
244+
[rangePaths removeObject:path];
245+
}
246+
}
247+
_rangeTypeIndexPaths[rangeKey] = rangePaths;
248+
}
249+
234250
[_delegate rangeController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
235251
});
236252
}

examples/VerticalWithinHorizontalScrolling/Sample/GradientTableNode.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,6 @@
1919

2020
- (instancetype)initWithElementSize:(CGSize)size;
2121

22+
@property (nonatomic) NSInteger pageNumber;
23+
2224
@end

examples/VerticalWithinHorizontalScrolling/Sample/GradientTableNode.mm

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,16 @@ - (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSInde
6363
{
6464
RandomCoreGraphicsNode *elementNode = [[RandomCoreGraphicsNode alloc] init];
6565
elementNode.preferredFrameSize = _elementSize;
66+
elementNode.indexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:_pageNumber];
6667
return elementNode;
6768
}
6869

70+
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
71+
{
72+
[tableView deselectRowAtIndexPath:indexPath animated:NO];
73+
[_tableNode.view reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
74+
}
75+
6976
- (void)layout
7077
{
7178
[super layout];

examples/VerticalWithinHorizontalScrolling/Sample/RandomCoreGraphicsNode.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@
1010

1111
@interface RandomCoreGraphicsNode : ASCellNode
1212

13+
@property (nonatomic) NSIndexPath *indexPath;
14+
1315
@end

0 commit comments

Comments
 (0)