1717#import " ASInternalHelpers.h"
1818#import " ASDisplayNode+FrameworkPrivate.h"
1919
20- extern BOOL ASInterfaceStateIncludesVisible (ASInterfaceState interfaceState)
21- {
22- return ((interfaceState & ASInterfaceStateVisible) == ASInterfaceStateVisible);
23- }
24-
25- extern BOOL ASInterfaceStateIncludesDisplay (ASInterfaceState interfaceState)
26- {
27- return ((interfaceState & ASInterfaceStateDisplay) == ASInterfaceStateDisplay);
28- }
29-
30- extern BOOL ASInterfaceStateIncludesFetchData (ASInterfaceState interfaceState)
31- {
32- return ((interfaceState & ASInterfaceStateFetchData) == ASInterfaceStateFetchData);
33- }
34-
3520@interface ASRangeControllerBeta ()
3621{
3722 BOOL _rangeIsValid;
@@ -95,36 +80,48 @@ - (void)_updateVisibleNodeIndexPaths
9580 [_layoutController setVisibleNodeIndexPaths: visibleNodePaths];
9681 }
9782
98- ASInterfaceState selfInterfaceState = [_dataSource interfaceStateForRangeController: self ];
83+ NSArray *allNodes = [_dataSource completedNodes ];
84+ NSArray *currentSectionNodes = nil ;
85+ NSInteger currentSectionIndex = -1 ; // Will be unequal to any indexPath.section, so we set currentSectionNodes.
9986
100- NSSet *visibleIndexPaths = [_layoutController indexPathsForScrolling: _scrollDirection rangeType: ASLayoutRangeTypeVisible];
87+ NSUInteger numberOfSections = [allNodes count ];
88+ NSUInteger numberOfNodesInSection = 0 ;
10189
102- #if RangeControllerLoggingEnabled
103- NSMutableArray *modified = [NSMutableArray array ];
104- #endif
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 );
96+
97+ ASInterfaceState selfInterfaceState = [_dataSource interfaceStateForRangeController: self ];
10598
10699 if (ASInterfaceStateIncludesVisible (selfInterfaceState)) {
107100 // If we are already visible, get busy! Better get started on preloading before the user scrolls more...
108- NSSet * fetchDataIndexPaths = [_layoutController indexPathsForScrolling: _scrollDirection rangeType: ASLayoutRangeTypeFetchData];
109- NSSet * displayIndexPaths = [_layoutController indexPathsForScrolling: _scrollDirection rangeType: ASLayoutRangeTypeDisplay];
101+ fetchDataIndexPaths = [_layoutController indexPathsForScrolling: _scrollDirection rangeType: ASLayoutRangeTypeFetchData];
102+ displayIndexPaths = [_layoutController indexPathsForScrolling: _scrollDirection rangeType: ASLayoutRangeTypeDisplay];
110103
111104 // Typically the fetchDataIndexPaths will be the largest, and be a superset of the others, though it may be disjoint.
112- NSMutableSet * allIndexPaths = [fetchDataIndexPaths mutableCopy ];
105+ allIndexPaths = [fetchDataIndexPaths mutableCopy ];
113106 [allIndexPaths unionSet: displayIndexPaths];
114107 [allIndexPaths unionSet: visibleIndexPaths];
108+ } else {
109+ allIndexPaths = [visibleIndexPaths mutableCopy ];
110+ }
111+
112+ // Sets are magical. Add anything we had applied interfaceState to in the last update, so we can clear any
113+ // 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 ];
116+ [allIndexPaths unionSet: _allPreviousIndexPaths];
117+ _allPreviousIndexPaths = allCurrentIndexPaths;
118+
119+ for (NSIndexPath *indexPath in allIndexPaths) {
120+ // Before a node / indexPath is exposed to ASRangeController, ASDataController should have already measured it.
121+ // For consistency, make sure each node knows that it should measure itself if something changes.
122+ ASInterfaceState interfaceState = ASInterfaceStateMeasureLayout;
115123
116- // Sets are magical. Add anything we had applied interfaceState to in the last update, so we can clear any
117- // range flags it still has enabled. Most of the time, all but a few elements are equal; a large programmatic
118- // scroll or major main thread stall could cause entirely disjoint sets, but we must visit all.
119- NSSet *allCurrentIndexPaths = [allIndexPaths copy ];
120- [allIndexPaths unionSet: _allPreviousIndexPaths];
121- _allPreviousIndexPaths = allCurrentIndexPaths;
122-
123- for (NSIndexPath *indexPath in allIndexPaths) {
124- // Before a node / indexPath is exposed to ASRangeController, ASDataController should have already measured it.
125- // For consistency, make sure each node knows that it should measure itself if something changes.
126- ASInterfaceState interfaceState = ASInterfaceStateMeasureLayout;
127-
124+ if (ASInterfaceStateIncludesVisible (selfInterfaceState)) {
128125 if ([fetchDataIndexPaths containsObject: indexPath]) {
129126 interfaceState |= ASInterfaceStateFetchData;
130127 }
@@ -134,39 +131,49 @@ - (void)_updateVisibleNodeIndexPaths
134131 if ([visibleIndexPaths containsObject: indexPath]) {
135132 interfaceState |= ASInterfaceStateVisible;
136133 }
137-
138- ASDisplayNode *node = [_dataSource rangeController: self nodeAtIndexPath: indexPath];
139- ASDisplayNodeAssert (node.hierarchyState & ASHierarchyStateRangeManaged, @" All nodes reaching this point should be range-managed, or interfaceState may be incorrectly reset." );
140- // Skip the many method calls of the recursive operation if the top level cell node already has the right interfaceState.
141- if (node.interfaceState != interfaceState) {
142- #if RangeControllerLoggingEnabled
143- [modified addObject: indexPath];
144- #endif
145- [node recursivelySetInterfaceState: interfaceState];
134+ } else {
135+ // If selfInterfaceState isn't visible, then visibleIndexPaths represents what /will/ be immediately visible at the
136+ // instant we come onscreen. So, fetch data and display all of those things, but don't waste resources preloading yet.
137+ // We handle this as a separate case to minimize set operations for offscreen preloading, including containsObject:.
138+
139+ // Set Layout, Fetch Data, Display. DO NOT set Visible: even though these elements are in the visible range / "viewport",
140+ // our overall container object is itself not visible yet. The moment it becomes visible, we will run the condition above.
141+ if ([allCurrentIndexPaths containsObject: indexPath]) {
142+ // We might be looking at an indexPath that was previously in-range, but now we need to clear it.
143+ // In that case we'll just set it back to MeasureLayout. Only set Display | FetchData if in allCurrentIndexPaths.
144+ interfaceState |= ASInterfaceStateDisplay;
145+ interfaceState |= ASInterfaceStateFetchData;
146146 }
147147 }
148- } else {
149- // If selfInterfaceState isn't visible, then visibleIndexPaths represents what /will/ be immediately visible at the
150- // instant we come onscreen. So, fetch data and display all of those things, but don't waste resources preloading yet.
151- // We handle this as a separate case to minimize set operations for offscreen preloading, including containsObject:.
152148
153- for (NSIndexPath *indexPath in visibleIndexPaths) {
154- // Set Layout, Fetch Data, Display. DO NOT set Visible: even though these elements are in the visible range / "viewport",
155- // our overall container object is itself not visible yet. The moment it becomes visible, we will run the condition above.
156- ASInterfaceState interfaceState = ASInterfaceStateMeasureLayout | ASInterfaceStateFetchData | ASInterfaceStateDisplay;
149+ NSInteger section = indexPath.section ;
150+ NSInteger row = indexPath.row ;
151+
152+ if (section >= 0 && row >= 0 && section < numberOfSections) {
153+ if (section != currentSectionIndex) {
154+ // Often we'll be dealing with indexPaths in the same section, but the set isn't sorted and we may even bounce
155+ // between the same ones. Still, this saves dozens of method calls to access the inner array and count.
156+ currentSectionNodes = [allNodes objectAtIndex: section];
157+ numberOfNodesInSection = [currentSectionNodes count ];
158+ currentSectionIndex = section;
159+ }
157160
158- ASDisplayNode *node = [_dataSource rangeController: self nodeAtIndexPath: indexPath];
159- ASDisplayNodeAssert (node.hierarchyState & ASHierarchyStateRangeManaged, @" All nodes reaching this point should be range-managed, or interfaceState may be incorrectly reset." );
160- // Skip the many method calls of the recursive operation if the top level cell node already has the right interfaceState.
161- if (node.interfaceState != interfaceState) {
162- #if RangeControllerLoggingEnabled
163- [modified addObject: indexPath];
164- #endif
165- [node recursivelySetInterfaceState: interfaceState];
161+ if (row < numberOfNodesInSection) {
162+ ASDisplayNode *node = [currentSectionNodes objectAtIndex: row];
163+
164+ ASDisplayNodeAssert (node.hierarchyState & ASHierarchyStateRangeManaged, @" All nodes reaching this point should be range-managed, or interfaceState may be incorrectly reset." );
165+ // Skip the many method calls of the recursive operation if the top level cell node already has the right interfaceState.
166+ if (node.interfaceState != interfaceState) {
167+ [modifiedIndexPaths addObject: indexPath];
168+ [node recursivelySetInterfaceState: interfaceState];
169+ }
166170 }
167171 }
168172 }
169173
174+ _rangeIsValid = YES ;
175+ _queuedRangeUpdate = NO ;
176+
170177#if RangeControllerLoggingEnabled
171178 NSSet *visibleNodePathsSet = [NSSet setWithArray: visibleNodePaths];
172179 BOOL setsAreEqual = [visibleIndexPaths isEqualToSet: visibleNodePathsSet];
@@ -176,9 +183,9 @@ - (void)_updateVisibleNodeIndexPaths
176183 NSLog (@" custom: %@ " , visibleNodePathsSet);
177184 }
178185
179- [modified sortUsingSelector: @selector (compare: )];
186+ [modifiedIndexPaths sortUsingSelector: @selector (compare: )];
180187
181- for (NSIndexPath *indexPath in modified ) {
188+ for (NSIndexPath *indexPath in modifiedIndexPaths ) {
182189 ASDisplayNode *node = [_dataSource rangeController: self nodeAtIndexPath: indexPath];
183190 ASInterfaceState interfaceState = node.interfaceState ;
184191 BOOL inVisible = ASInterfaceStateIncludesVisible (interfaceState);
@@ -187,9 +194,6 @@ - (void)_updateVisibleNodeIndexPaths
187194 NSLog (@" indexPath %@ , Visible: %d , Display: %d , FetchData: %d " , indexPath, inVisible, inDisplay, inFetchData);
188195 }
189196#endif
190-
191- _rangeIsValid = YES ;
192- _queuedRangeUpdate = NO ;
193197}
194198
195199#pragma mark - Cell node view handling
0 commit comments