diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index 997ae4ff63..2065a9fcf4 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -338,6 +338,13 @@ */ @property (nonatomic, assign) BOOL placeholderEnabled; +/** + * @abstract Toggle to fade-out the placeholder when a node's contents are finished displaying. + * + * @discussion Defaults to NO. + */ +@property (nonatomic, assign) BOOL placeholderFadesOut; + /** @name Hit Testing */ diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 30a1a1fe2f..53ef2b81c5 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -116,6 +116,8 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)()) _flags.implementsDrawRect = ([[self class] respondsToSelector:@selector(drawRect:withParameters:isCancelled:isRasterizing:)] ? 1 : 0); _flags.implementsImageDisplay = ([[self class] respondsToSelector:@selector(displayWithParameters:isCancelled:)] ? 1 : 0); _flags.implementsDrawParameters = ([self respondsToSelector:@selector(drawParametersForAsyncLayer:)] ? 1 : 0); + + _fadeAnimationDuration = 0.1; } - (id)init @@ -1133,7 +1135,19 @@ static NSInteger incrementIfFound(NSInteger i) { // only trampoline if there is a placeholder and nodes are done displaying if ([self _pendingDisplayNodesHaveFinished] && _placeholderLayer.superlayer) { dispatch_async(dispatch_get_main_queue(), ^{ - [self _tearDownPlaceholderLayer]; + void (^cleanupBlock)() = ^{ + [self _tearDownPlaceholderLayer]; + }; + + if (self.placeholderFadesOut) { + [CATransaction begin]; + [CATransaction setCompletionBlock:cleanupBlock]; + [CATransaction setAnimationDuration:_fadeAnimationDuration]; + _placeholderLayer.opacity = 0.0; + [CATransaction commit]; + } else { + cleanupBlock(); + } }); } } @@ -1257,10 +1271,6 @@ static NSInteger incrementIfFound(NSInteger i) { [self _pendingNodeDidDisplay:self]; [_supernode subnodeDisplayDidFinish:self]; - - if (_placeholderLayer && [self _pendingDisplayNodesHaveFinished]) { - [self _tearDownPlaceholderLayer]; - } } - (void)subnodeDisplayWillStart:(ASDisplayNode *)subnode diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index 2995caab2b..501324e409 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -61,6 +61,8 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)()); _ASPendingState *_pendingViewState; + NSTimeInterval _fadeAnimationDuration; + struct { // public properties unsigned synchronous:1; diff --git a/examples/Placeholders/Sample/SlowpokeImageNode.m b/examples/Placeholders/Sample/SlowpokeImageNode.m index 07ba36cd46..6f2ae243fc 100644 --- a/examples/Placeholders/Sample/SlowpokeImageNode.m +++ b/examples/Placeholders/Sample/SlowpokeImageNode.m @@ -28,6 +28,7 @@ static CGFloat const kASDKLogoAspectRatio = 2.79; { if (self = [super init]) { self.placeholderEnabled = YES; + self.fadeOutPlaceholder = YES; } return self; } @@ -46,8 +47,11 @@ static CGFloat const kASDKLogoAspectRatio = 2.79; { CGSize size = self.calculatedSize; UIGraphicsBeginImageContext(size); + [[UIColor whiteColor] setFill]; [[UIColor colorWithWhite:0.9 alpha:1] setStroke]; + UIRectFill((CGRect){CGPointZero, size}); + UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:CGPointZero]; [path addLineToPoint:(CGPoint){size.width, size.height}];