Merge pull request #127 from facebook/manual-display

Add -[ASDisplayNode display].
This commit is contained in:
Nadine Salter
2014-12-02 17:49:55 -08:00
4 changed files with 58 additions and 28 deletions

2
.gitignore vendored
View File

@@ -22,3 +22,5 @@ docs/htdocs
docs/.sass-cache
*.swp
*.lock

View File

@@ -294,6 +294,19 @@
*/
@property (nonatomic, assign) BOOL shouldRasterizeDescendants;
/**
* @abstract Calls -setNeedsDisplay and -displayIfNeeded on the node's backing store.
*
* @note This method must be called on the main thread but there are plans to allow this on any thread.
*/
- (void)display;
/**
* @abstract Call -display on the node and recursively on all subnodes, forcing the entire node hierarchy to be
* displayed.
*/
- (void)recursivelyDisplay;
/**
* @abstract Display the node's view/layer immediately on the current thread, bypassing the background thread rendering.
*/

View File

@@ -437,6 +437,33 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)())
_contentsScaleForDisplay = contentsScaleForDisplay;
}
- (void)display
{
ASDisplayNodeAssertMainThread();
ASDisplayNodeAssert(self.nodeLoaded, @"backing store must be loaded before calling -display");
// rendering a backing store requires a node be laid out
[self __layout];
CALayer *layer = [self isLayerBacked] ? self.layer : self.view.layer;
if (layer.contents) {
return;
}
[layer setNeedsDisplay];
[layer displayIfNeeded];
}
- (void)recursivelyDisplay
{
for (ASDisplayNode *node in self.subnodes) {
[node recursivelyDisplay];
}
[self display];
}
- (void)displayImmediately
{
ASDisplayNodeAssertMainThread();

View File

@@ -91,22 +91,6 @@
return sizingQueue;
}
+ (UIView *)workingView
{
// we add nodes' views to this invisible window to start async rendering
static UIWindow *workingWindow = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
workingWindow = [[UIWindow alloc] initWithFrame:CGRectZero];
workingWindow.windowLevel = UIWindowLevelNormal - 1000;
workingWindow.userInteractionEnabled = NO;
workingWindow.clipsToBounds = YES;
workingWindow.hidden = YES;
});
return workingWindow;
}
#pragma mark -
#pragma mark Helpers.
@@ -191,47 +175,51 @@ static BOOL ASRangeIsValid(NSRange range)
NSInteger index = [self indexForIndexPath:node.asyncdisplaykit_indexPath];
if (NSLocationInRange(index, _workingRange)) {
// move the node's view to the working range area, so its rendering persists
[self moveNodeToWorkingView:node];
[self addNodeToWorkingRange:node];
} else {
// this node isn't in the working range, remove it from the view hierarchy
[self removeNodeFromWorkingView:node];
[self removeNodeFromWorkingRange:node];
}
}
- (void)removeNodeFromWorkingView:(ASCellNode *)node
- (void)removeNodeFromWorkingRange:(ASCellNode *)node
{
ASDisplayNodeAssertMainThread();
ASDisplayNodeAssert(node, @"invalid argument");
[node recursiveSetPreventOrCancelDisplay:YES];
[node.view removeFromSuperview];
// since this class usually manages large or infinite data sets, the working range
// directly bounds memory usage by requiring redrawing any content that falls outside the range.
[node recursivelyReclaimMemory];
[_workingIndexPaths removeObject:node.asyncdisplaykit_indexPath];
}
- (void)moveNodeToWorkingView:(ASCellNode *)node
- (void)addNodeToWorkingRange:(ASCellNode *)node
{
ASDisplayNodeAssertMainThread();
ASDisplayNodeAssert(node, @"invalid argument");
[self moveNode:node toView:[ASRangeController workingView]];
// if node is in the working range it should not actively be in view
[node.view removeFromSuperview];
[node recursivelyDisplay];
[_workingIndexPaths addObject:node.asyncdisplaykit_indexPath];
}
- (void)moveNode:(ASCellNode *)node toView:(UIView *)view
{
ASDisplayNodeAssertMainThread();
ASDisplayNodeAssert(node && view, @"invalid argument, did you mean -removeNodeFromWorkingView:?");
ASDisplayNodeAssert(node && view, @"invalid argument, did you mean -removeNodeFromWorkingRange:?");
// use an explicit transaction to force CoreAnimation to display nodes in the order they are added.
[CATransaction begin];
[view addSubview:node.view];
[CATransaction commit];
}
@@ -481,7 +469,7 @@ static NSRange ASCalculateWorkingRange(ASRangeTuningParameters params, ASScrollD
_nodeSizes,
[_delegate rangeControllerViewportSize:self]);
}
[self setWorkingRange:workingRange];
}
@@ -504,7 +492,7 @@ static NSRange ASCalculateWorkingRange(ASRangeTuningParameters params, ASScrollD
for (NSIndexPath *indexPath in removedIndexPaths) {
ASCellNode *node = [self sizedNodeForIndexPath:indexPath];
ASDisplayNodeAssert(node, @"an unsized node should never have entered the working range");
[self removeNodeFromWorkingView:node];
[self removeNodeFromWorkingRange:node];
}
// add nodes that have entered the working range (i.e., those that are in the new working range but not the old one)
@@ -513,7 +501,7 @@ static NSRange ASCalculateWorkingRange(ASRangeTuningParameters params, ASScrollD
// if a node in the working range is still sizing, the sizing logic will add it to the working range for us later
ASCellNode *node = [self sizedNodeForIndexPath:indexPath];
if (node) {
[self moveNodeToWorkingView:node];
[self addNodeToWorkingRange:node];
} else {
ASDisplayNodeAssert(_sizedNodeCount != _totalNodeCount, @"logic error");
}