@@ -21,8 +21,9 @@ @interface ASRangeControllerBeta ()
2121{
2222 BOOL _rangeIsValid;
2323 BOOL _queuedRangeUpdate;
24+ BOOL _layoutControllerImplementsSetVisibleIndexPaths;
2425 ASScrollDirection _scrollDirection;
25- NSSet *_allPreviousIndexPaths;
26+ NSSet < NSIndexPath *> *_allPreviousIndexPaths;
2627}
2728
2829@end
@@ -58,41 +59,50 @@ - (void)visibleNodeIndexPathsDidChangeWithScrollDirection:(ASScrollDirection)scr
5859 });
5960}
6061
62+ - (void )setLayoutController : (id <ASLayoutController>)layoutController
63+ {
64+ _layoutController = layoutController;
65+ _layoutControllerImplementsSetVisibleIndexPaths = [_layoutController respondsToSelector: @selector (setVisibleNodeIndexPaths: )];
66+ }
67+
6168- (void )_updateVisibleNodeIndexPaths
6269{
63- if (!_queuedRangeUpdate) {
70+ ASDisplayNodeAssert (_layoutController, @" An ASLayoutController is required by ASRangeController" );
71+ if (!_queuedRangeUpdate || !_layoutController) {
6472 return ;
6573 }
6674
67- // FIXME: Consider if we need to check this separately from the range calculation below.
68- NSArray *visibleNodePaths = [_dataSource visibleNodeIndexPathsForRangeController: self ];
75+ // TODO: Consider if we need to use this codepath, or can rely on something more similar to the data & display ranges
76+ // Example: ... = [_layoutController indexPathsForScrolling:_scrollDirection rangeType:ASLayoutRangeTypeVisible];
77+ NSArray <NSIndexPath *> *visibleNodePaths = [_dataSource visibleNodeIndexPathsForRangeController: self ];
6978
7079 if (visibleNodePaths.count == 0 ) { // if we don't have any visibleNodes currently (scrolled before or after content)...
7180 _queuedRangeUpdate = NO ;
7281 return ; // don't do anything for this update, but leave _rangeIsValid == NO to make sure we update it later
7382 }
7483
75- CGSize viewportSize = [_dataSource viewportSizeForRangeController: self ];
76- [_layoutController setViewportSize: viewportSize];
84+ [_layoutController setViewportSize: [_dataSource viewportSizeForRangeController: self ]];
7785
7886 // the layout controller needs to know what the current visible indices are to calculate range offsets
79- if ([_layoutController respondsToSelector: @selector ( setVisibleNodeIndexPaths: )] ) {
87+ if (_layoutControllerImplementsSetVisibleIndexPaths ) {
8088 [_layoutController setVisibleNodeIndexPaths: visibleNodePaths];
8189 }
8290
83- NSArray *allNodes = [_dataSource completedNodes ];
84- NSArray *currentSectionNodes = nil ;
85- NSInteger currentSectionIndex = -1 ; // Will be unequal to any indexPath.section, so we set currentSectionNodes.
86-
91+ // allNodes is a 2D array: it contains arrays for each section, each containing nodes.
92+ NSArray <NSArray *> *allNodes = [_dataSource completedNodes ];
8793 NSUInteger numberOfSections = [allNodes count ];
94+
95+ NSArray <ASDisplayNode *> *currentSectionNodes = nil ;
96+ NSInteger currentSectionIndex = -1 ; // Set to -1 so we don't match any indexPath.section on the first iteration.
8897 NSUInteger numberOfNodesInSection = 0 ;
8998
90- NSSet *visibleIndexPaths = [NSSet setWithArray: visibleNodePaths];
91- // = [_layoutController indexPathsForScrolling:_scrollDirection rangeType:ASLayoutRangeTypeVisible];
92- NSSet *displayIndexPaths = nil ;
93- NSSet *fetchDataIndexPaths = nil ;
94- NSMutableSet *allIndexPaths = nil ;
95- NSMutableArray *modifiedIndexPaths = (RangeControllerLoggingEnabled ? [NSMutableArray array ] : nil );
99+ NSSet <NSIndexPath *> *visibleIndexPaths = [NSSet setWithArray: visibleNodePaths];
100+ NSSet <NSIndexPath *> *displayIndexPaths = nil ;
101+ NSSet <NSIndexPath *> *fetchDataIndexPaths = nil ;
102+
103+ // Prioritize the order in which we visit each. Visible nodes should be updated first so they are enqueued on
104+ // the network or display queues before preloading (offscreen) nodes are enqueued.
105+ NSMutableOrderedSet <NSIndexPath *> *allIndexPaths = [[NSMutableOrderedSet alloc ] initWithSet: visibleIndexPaths];
96106
97107 ASInterfaceState selfInterfaceState = [_dataSource interfaceStateForRangeController: self ];
98108
@@ -102,20 +112,23 @@ - (void)_updateVisibleNodeIndexPaths
102112 displayIndexPaths = [_layoutController indexPathsForScrolling: _scrollDirection rangeType: ASLayoutRangeTypeDisplay];
103113
104114 // Typically the fetchDataIndexPaths will be the largest, and be a superset of the others, though it may be disjoint.
105- allIndexPaths = [fetchDataIndexPaths mutableCopy ];
115+ // Because allIndexPaths is an NSMutableOrderedSet, this adds the non-duplicate items /after/ the existing items.
116+ // This means that during iteration, we will first visit visible, then display, then fetch data nodes.
106117 [allIndexPaths unionSet: displayIndexPaths];
107- [allIndexPaths unionSet: visibleIndexPaths];
108- } else {
109- allIndexPaths = [visibleIndexPaths mutableCopy ];
118+ [allIndexPaths unionSet: fetchDataIndexPaths];
110119 }
111120
112- // Sets are magical. Add anything we had applied interfaceState to in the last update, so we can clear any
121+ // Add anything we had applied interfaceState to in the last update, but is no longer in range , so we can clear any
113122 // range flags it still has enabled. Most of the time, all but a few elements are equal; a large programmatic
114- // scroll or major main thread stall could cause entirely disjoint sets, but we must visit all.
115- NSSet *allCurrentIndexPaths = [allIndexPaths copy ];
123+ // scroll or major main thread stall could cause entirely disjoint sets. In either case we must visit all.
124+ // Calling "-set" on NSMutableOrderedSet just references the underlying mutable data store, so we must copy it.
125+ NSSet <NSIndexPath *> *allCurrentIndexPaths = [[allIndexPaths set ] copy ];
116126 [allIndexPaths unionSet: _allPreviousIndexPaths];
117127 _allPreviousIndexPaths = allCurrentIndexPaths;
118128
129+ // This array is only used if logging is enabled.
130+ NSMutableArray <NSIndexPath *> *modifiedIndexPaths = (RangeControllerLoggingEnabled ? [NSMutableArray array ] : nil );
131+
119132 for (NSIndexPath *indexPath in allIndexPaths) {
120133 // Before a node / indexPath is exposed to ASRangeController, ASDataController should have already measured it.
121134 // For consistency, make sure each node knows that it should measure itself if something changes.
0 commit comments