|
17 | 17 | #import "ASInternalHelpers.h" |
18 | 18 | #import "ASDisplayNode+FrameworkPrivate.h" |
19 | 19 |
|
| 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 | + |
20 | 35 | @interface ASRangeControllerBeta () |
21 | 36 | { |
22 | 37 | BOOL _rangeIsValid; |
@@ -79,52 +94,91 @@ - (void)_updateVisibleNodeIndexPaths |
79 | 94 | [_layoutController setVisibleNodeIndexPaths:visibleNodePaths]; |
80 | 95 | } |
81 | 96 |
|
82 | | - NSSet *fetchDataIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection rangeType:ASLayoutRangeTypeFetchData]; |
83 | | - NSSet *displayIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection rangeType:ASLayoutRangeTypeDisplay]; |
84 | | - NSSet *visibleIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection rangeType:ASLayoutRangeTypeVisible]; |
85 | | - |
86 | | - //NSSet *visibleNodePathsSet = [NSSet setWithArray:visibleNodePaths]; |
87 | | - //NSLog(@"visible sets are equal: %d", [visibleIndexPaths isEqualToSet:visibleNodePathsSet]); |
| 97 | + ASInterfaceState selfInterfaceState = [_dataSource interfaceStateForRangeController:self]; |
88 | 98 |
|
89 | | - // Typically the fetchDataIndexPaths will be the largest, and be a superset of the others, though it may be disjoint. |
90 | | - NSMutableSet *allIndexPaths = [fetchDataIndexPaths mutableCopy]; |
91 | | - [allIndexPaths unionSet:displayIndexPaths]; |
92 | | - [allIndexPaths unionSet:visibleIndexPaths]; |
| 99 | + NSSet *visibleIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection rangeType:ASLayoutRangeTypeVisible]; |
93 | 100 |
|
| 101 | +#if RangeControllerLoggingEnabled |
94 | 102 | NSMutableArray *modified = [NSMutableArray array]; |
| 103 | +#endif |
| 104 | + |
| 105 | + if (ASInterfaceStateIncludesVisible(selfInterfaceState)) { |
| 106 | + // If we are already visible, get busy! Better get started on preloading before the user scrolls more... |
| 107 | + NSSet *fetchDataIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection rangeType:ASLayoutRangeTypeFetchData]; |
| 108 | + NSSet *displayIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection rangeType:ASLayoutRangeTypeDisplay]; |
95 | 109 |
|
96 | | - for (NSIndexPath *indexPath in allIndexPaths) { |
97 | | - // Before a node / indexPath is exposed to ASRangeController, ASDataController should have already measured it. |
98 | | - // For consistency, make sure each node knows that it should measure itself if something changes. |
99 | | - ASInterfaceState interfaceState = ASInterfaceStateMeasureLayout; |
| 110 | + // Typically the fetchDataIndexPaths will be the largest, and be a superset of the others, though it may be disjoint. |
| 111 | + NSMutableSet *allIndexPaths = [fetchDataIndexPaths mutableCopy]; |
| 112 | + [allIndexPaths unionSet:displayIndexPaths]; |
| 113 | + [allIndexPaths unionSet:visibleIndexPaths]; |
100 | 114 |
|
101 | | - if ([fetchDataIndexPaths containsObject:indexPath]) { |
102 | | - interfaceState |= ASInterfaceStateFetchData; |
103 | | - } |
104 | | - if ([displayIndexPaths containsObject:indexPath]) { |
105 | | - interfaceState |= ASInterfaceStateDisplay; |
106 | | - } |
107 | | - if ([visibleIndexPaths containsObject:indexPath]) { |
108 | | - interfaceState |= ASInterfaceStateVisible; |
| 115 | + for (NSIndexPath *indexPath in allIndexPaths) { |
| 116 | + // Before a node / indexPath is exposed to ASRangeController, ASDataController should have already measured it. |
| 117 | + // For consistency, make sure each node knows that it should measure itself if something changes. |
| 118 | + ASInterfaceState interfaceState = ASInterfaceStateMeasureLayout; |
| 119 | + |
| 120 | + if ([fetchDataIndexPaths containsObject:indexPath]) { |
| 121 | + interfaceState |= ASInterfaceStateFetchData; |
| 122 | + } |
| 123 | + if ([displayIndexPaths containsObject:indexPath]) { |
| 124 | + interfaceState |= ASInterfaceStateDisplay; |
| 125 | + } |
| 126 | + if ([visibleIndexPaths containsObject:indexPath]) { |
| 127 | + interfaceState |= ASInterfaceStateVisible; |
| 128 | + } |
| 129 | + |
| 130 | + ASDisplayNode *node = [_dataSource rangeController:self nodeAtIndexPath:indexPath]; |
| 131 | + ASDisplayNodeAssert(node.hierarchyState & ASHierarchyStateRangeManaged, @"All nodes reaching this point should be range-managed, or interfaceState may be incorrectly reset."); |
| 132 | + // Skip the many method calls of the recursive operation if the top level cell node already has the right interfaceState. |
| 133 | + if (node.interfaceState != interfaceState) { |
| 134 | +#if RangeControllerLoggingEnabled |
| 135 | + [modified addObject:indexPath]; |
| 136 | +#endif |
| 137 | + [node recursivelySetInterfaceState:interfaceState]; |
| 138 | + } |
109 | 139 | } |
| 140 | + } else { |
| 141 | + // If selfInterfaceState isn't visible, then visibleIndexPaths represents what /will/ be immediately visible at the |
| 142 | + // instant we come onscreen. So, fetch data and display all of those things, but don't waste resources preloading yet. |
| 143 | + // We handle this as a separate case to minimize set operations for offscreen preloading, including containsObject:. |
110 | 144 |
|
111 | | - ASDisplayNode *node = [_dataSource rangeController:self nodeAtIndexPath:indexPath]; |
112 | | - ASDisplayNodeAssert(node.hierarchyState & ASHierarchyStateRangeManaged, @"All nodes reaching this point should be range-managed, or interfaceState may be incorrectly reset."); |
113 | | - // Skip the many method calls of the recursive operation if the top level cell node already has the right interfaceState. |
114 | | - if (node.interfaceState != interfaceState) { |
115 | | - [modified addObject:indexPath]; |
116 | | - [node recursivelySetInterfaceState:interfaceState]; |
| 145 | + for (NSIndexPath *indexPath in visibleIndexPaths) { |
| 146 | + // Set Layout, Fetch Data, Display. DO NOT set Visible: even though these elements are in the visible range / "viewport", |
| 147 | + // our overall container object is itself not visible yet. The moment it becomes visible, we will run the condition above. |
| 148 | + ASInterfaceState interfaceState = ASInterfaceStateMeasureLayout | ASInterfaceStateFetchData | ASInterfaceStateDisplay; |
| 149 | + |
| 150 | + ASDisplayNode *node = [_dataSource rangeController:self nodeAtIndexPath:indexPath]; |
| 151 | + ASDisplayNodeAssert(node.hierarchyState & ASHierarchyStateRangeManaged, @"All nodes reaching this point should be range-managed, or interfaceState may be incorrectly reset."); |
| 152 | + // Skip the many method calls of the recursive operation if the top level cell node already has the right interfaceState. |
| 153 | + if (node.interfaceState != interfaceState) { |
| 154 | +#if RangeControllerLoggingEnabled |
| 155 | + [modified addObject:indexPath]; |
| 156 | +#endif |
| 157 | + [node recursivelySetInterfaceState:interfaceState]; |
| 158 | + } |
117 | 159 | } |
118 | 160 | } |
119 | 161 |
|
120 | | -/* |
| 162 | +#if RangeControllerLoggingEnabled |
| 163 | + NSSet *visibleNodePathsSet = [NSSet setWithArray:visibleNodePaths]; |
| 164 | + BOOL setsAreEqual = [visibleIndexPaths isEqualToSet:visibleNodePathsSet]; |
| 165 | + NSLog(@"visible sets are equal: %d", setsAreEqual); |
| 166 | + if (!setsAreEqual) { |
| 167 | + NSLog(@"standard: %@", visibleIndexPaths); |
| 168 | + NSLog(@"custom: %@", visibleNodePathsSet); |
| 169 | + } |
| 170 | + |
121 | 171 | [modified sortUsingSelector:@selector(compare:)]; |
122 | 172 |
|
123 | 173 | for (NSIndexPath *indexPath in modified) { |
124 | | - NSLog(@"indexPath %@, Visible: %d, Display: %d, FetchData: %d", indexPath, [visibleIndexPaths containsObject:indexPath], [displayIndexPaths containsObject:indexPath], [fetchDataIndexPaths containsObject:indexPath]); |
| 174 | + ASDisplayNode *node = [_dataSource rangeController:self nodeAtIndexPath:indexPath]; |
| 175 | + ASInterfaceState interfaceState = node.interfaceState; |
| 176 | + BOOL inVisible = ASInterfaceStateIncludesVisible(interfaceState); |
| 177 | + BOOL inDisplay = ASInterfaceStateIncludesDisplay(interfaceState); |
| 178 | + BOOL inFetchData = ASInterfaceStateIncludesFetchData(interfaceState); |
| 179 | + NSLog(@"indexPath %@, Visible: %d, Display: %d, FetchData: %d", indexPath, inVisible, inDisplay, inFetchData); |
125 | 180 | } |
126 | | -*/ |
127 | | - |
| 181 | +#endif |
128 | 182 |
|
129 | 183 | _rangeIsValid = YES; |
130 | 184 | _queuedRangeUpdate = NO; |
|
0 commit comments