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

Commit 7a9cd1f

Browse files
author
Scott Goodson
committed
[ASRangeController] Introduce totally rewritten range controller based on ASInterfaceState.
1 parent 8f914f8 commit 7a9cd1f

2 files changed

Lines changed: 230 additions & 0 deletions

File tree

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/* Copyright (c) 2014-present, Facebook, Inc.
2+
* All rights reserved.
3+
*
4+
* This source code is licensed under the BSD-style license found in the
5+
* LICENSE file in the root directory of this source tree. An additional grant
6+
* of patent rights can be found in the PATENTS file in the same directory.
7+
*/
8+
9+
#import <Foundation/Foundation.h>
10+
11+
#import <AsyncDisplayKit/ASRangeController.h>
12+
#import <AsyncDisplayKit/ASCellNode.h>
13+
#import <AsyncDisplayKit/ASDataController.h>
14+
#import <AsyncDisplayKit/ASLayoutController.h>
15+
16+
NS_ASSUME_NONNULL_BEGIN
17+
18+
// ASRangeControllerBeta defined in ASRangeController.h
19+
20+
NS_ASSUME_NONNULL_END
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
/* Copyright (c) 2014-present, Facebook, Inc.
2+
* All rights reserved.
3+
*
4+
* This source code is licensed under the BSD-style license found in the
5+
* LICENSE file in the root directory of this source tree. An additional grant
6+
* of patent rights can be found in the PATENTS file in the same directory.
7+
*/
8+
9+
#import "ASRangeControllerBeta.h"
10+
11+
#import "ASAssert.h"
12+
#import "ASDisplayNodeExtras.h"
13+
#import "ASMultiDimensionalArrayUtils.h"
14+
#import "ASRangeHandlerVisible.h"
15+
#import "ASRangeHandlerRender.h"
16+
#import "ASRangeHandlerPreload.h"
17+
#import "ASInternalHelpers.h"
18+
#import "ASDisplayNode+FrameworkPrivate.h"
19+
20+
@interface ASRangeControllerBeta ()
21+
{
22+
BOOL _rangeIsValid;
23+
BOOL _queuedRangeUpdate;
24+
ASScrollDirection _scrollDirection;
25+
}
26+
27+
@end
28+
29+
@implementation ASRangeControllerBeta
30+
31+
- (instancetype)init
32+
{
33+
if (!(self = [super init])) {
34+
return nil;
35+
}
36+
37+
_rangeIsValid = YES;
38+
39+
return self;
40+
}
41+
42+
#pragma mark - Core visible node range managment API
43+
44+
- (void)visibleNodeIndexPathsDidChangeWithScrollDirection:(ASScrollDirection)scrollDirection
45+
{
46+
_scrollDirection = scrollDirection;
47+
48+
if (_queuedRangeUpdate) {
49+
return;
50+
}
51+
52+
// coalesce these events -- handling them multiple times per runloop is noisy and expensive
53+
_queuedRangeUpdate = YES;
54+
55+
dispatch_async(dispatch_get_main_queue(), ^{
56+
[self _updateVisibleNodeIndexPaths];
57+
});
58+
}
59+
60+
- (void)_updateVisibleNodeIndexPaths
61+
{
62+
if (!_queuedRangeUpdate) {
63+
return;
64+
}
65+
66+
// FIXME: Consider if we need to check this separately from the range calculation below.
67+
NSArray *visibleNodePaths = [_dataSource visibleNodeIndexPathsForRangeController:self];
68+
69+
if (visibleNodePaths.count == 0) { // if we don't have any visibleNodes currently (scrolled before or after content)...
70+
_queuedRangeUpdate = NO;
71+
return; // don't do anything for this update, but leave _rangeIsValid == NO to make sure we update it later
72+
}
73+
74+
CGSize viewportSize = [_dataSource viewportSizeForRangeController:self];
75+
76+
// the layout controller needs to know what the current visible indices are to calculate range offsets
77+
if ([_layoutController respondsToSelector:@selector(setVisibleNodeIndexPaths:)]) {
78+
[_layoutController setVisibleNodeIndexPaths:visibleNodePaths];
79+
}
80+
81+
NSSet *fetchDataIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection
82+
viewportSize:viewportSize
83+
rangeType:ASLayoutRangeTypeFetchData];
84+
85+
NSSet *displayIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection
86+
viewportSize:viewportSize
87+
rangeType:ASLayoutRangeTypeDisplay];
88+
89+
NSSet *visibleIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection
90+
viewportSize:viewportSize
91+
rangeType:ASLayoutRangeTypeVisible];
92+
93+
NSSet *visibleNodePathsSet = [NSSet setWithArray:visibleNodePaths];
94+
// NSLog(@"visible sets are equal: %d", [visibleIndexPaths isEqualToSet:visibleNodePathsSet]);
95+
96+
// Typically the fetchDataIndexPaths will be the largest, and be a superset of the others, though it may be disjoint.
97+
NSMutableSet *allIndexPaths = [fetchDataIndexPaths mutableCopy];
98+
[allIndexPaths unionSet:displayIndexPaths];
99+
[allIndexPaths unionSet:visibleIndexPaths];
100+
101+
NSMutableArray *modified = [NSMutableArray array];
102+
103+
for (NSIndexPath *indexPath in allIndexPaths) {
104+
// Before a node / indexPath is exposed to ASRangeController, ASDataController should have already measured it.
105+
// For consistency, make sure each node knows that it should measure itself if something changes.
106+
ASInterfaceState interfaceState = ASInterfaceStateMeasureLayout;
107+
108+
if ([fetchDataIndexPaths containsObject:indexPath]) {
109+
interfaceState |= ASInterfaceStateFetchData;
110+
}
111+
if ([displayIndexPaths containsObject:indexPath]) {
112+
interfaceState |= ASInterfaceStateDisplay;
113+
}
114+
if ([visibleIndexPaths containsObject:indexPath]) {
115+
interfaceState |= ASInterfaceStateVisible;
116+
}
117+
118+
ASDisplayNode *node = [_dataSource rangeController:self nodeAtIndexPath:indexPath];
119+
ASDisplayNodeAssert(node.hierarchyState & ASHierarchyStateRangeManaged, @"All nodes reaching this point should be range-managed, or interfaceState may be incorrectly reset.");
120+
// Skip the many method calls of the recursive operation if the top level cell node already has the right interfaceState.
121+
if (node.interfaceState != interfaceState) {
122+
[modified addObject:indexPath];
123+
[node recursivelySetInterfaceState:interfaceState];
124+
}
125+
}
126+
127+
/*
128+
[modified sortUsingSelector:@selector(compare:)];
129+
130+
for (NSIndexPath *indexPath in modified) {
131+
NSLog(@"indexPath %@, Visible: %d, Display: %d, FetchData: %d", indexPath, [visibleIndexPaths containsObject:indexPath], [displayIndexPaths containsObject:indexPath], [fetchDataIndexPaths containsObject:indexPath]);
132+
}
133+
*/
134+
135+
_rangeIsValid = YES;
136+
_queuedRangeUpdate = NO;
137+
}
138+
139+
#pragma mark - Cell node view handling
140+
141+
- (void)configureContentView:(UIView *)contentView forCellNode:(ASCellNode *)node
142+
{
143+
ASDisplayNodeAssertMainThread();
144+
ASDisplayNodeAssert(node, @"Cannot move a nil node to a view");
145+
ASDisplayNodeAssert(contentView, @"Cannot move a node to a non-existent view");
146+
147+
if (node.view.superview == contentView) {
148+
// this content view is already correctly configured
149+
return;
150+
}
151+
152+
// clean the content view
153+
for (UIView *view in contentView.subviews) {
154+
[view removeFromSuperview];
155+
}
156+
157+
[contentView addSubview:node.view];
158+
}
159+
160+
#pragma mark - ASDataControllerDelegete
161+
162+
- (void)dataControllerBeginUpdates:(ASDataController *)dataController
163+
{
164+
ASPerformBlockOnMainThread(^{
165+
[_delegate didBeginUpdatesInRangeController:self];
166+
});
167+
}
168+
169+
- (void)dataController:(ASDataController *)dataController endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion
170+
{
171+
ASPerformBlockOnMainThread(^{
172+
[_delegate rangeController:self didEndUpdatesAnimated:animated completion:completion];
173+
});
174+
}
175+
176+
- (void)dataController:(ASDataController *)dataController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
177+
{
178+
ASDisplayNodeAssert(nodes.count == indexPaths.count, @"Invalid index path");
179+
ASPerformBlockOnMainThread(^{
180+
_rangeIsValid = NO;
181+
[_delegate rangeController:self didInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
182+
});
183+
}
184+
185+
- (void)dataController:(ASDataController *)dataController didDeleteNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
186+
{
187+
ASPerformBlockOnMainThread(^{
188+
_rangeIsValid = NO;
189+
[_delegate rangeController:self didDeleteNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
190+
});
191+
}
192+
193+
- (void)dataController:(ASDataController *)dataController didInsertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
194+
{
195+
ASDisplayNodeAssert(sections.count == indexSet.count, @"Invalid sections");
196+
ASPerformBlockOnMainThread(^{
197+
_rangeIsValid = NO;
198+
[_delegate rangeController:self didInsertSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
199+
});
200+
}
201+
202+
- (void)dataController:(ASDataController *)dataController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
203+
{
204+
ASPerformBlockOnMainThread(^{
205+
_rangeIsValid = NO;
206+
[_delegate rangeController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
207+
});
208+
}
209+
210+
@end

0 commit comments

Comments
 (0)