diff --git a/AsyncDisplayKit/ASDisplayNode+Beta.h b/AsyncDisplayKit/ASDisplayNode+Beta.h index 473b565f27..3691abb08d 100644 --- a/AsyncDisplayKit/ASDisplayNode+Beta.h +++ b/AsyncDisplayKit/ASDisplayNode+Beta.h @@ -6,6 +6,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +#import "_ASDisplayLayer.h" + @interface ASDisplayNode (Beta) + (BOOL)shouldUseNewRenderingRange; @@ -20,4 +22,25 @@ */ - (void)recursivelyEnsureDisplaySynchronously:(BOOL)synchronously; +- (void)drawRect:(CGRect)bounds withParameters:(id )parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing; + +- (UIImage *)displayWithParameters:(id )parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelled; + +/** + * @abstract allow modification of a context before the node's content is drawn + * + * @discussion Set the block to be called after the context has been created and before the node's content is drawn. + * You can override this to modify the context before the content is drawn. You are responsible for saving and + * restoring context if necessary. Restoring can be done in contextDidDisplayNodeContent + * This block can be called from *any* thread and it is unsafe to access any UIKit main thread properties from it. + */ +@property (nonatomic, strong) ASDisplayNodeContextModifier willDisplayNodeContentBlock; + +/** + * @abstract allow modification of a context after the node's content is drawn + * + * @discussion + */ +@property (nonatomic, strong) ASDisplayNodeContextModifier didDisplayNodeContentBlock; + @end diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index 524b977fb7..701df672ad 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -37,6 +37,11 @@ typedef CALayer * _Nonnull(^ASDisplayNodeLayerBlock)(); */ typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode * _Nonnull node); +/** + * ASDisplayNode will / did render node content in context. + */ +typedef void (^ASDisplayNodeContextModifier)(CGContextRef context); + /** Interface state is available on ASDisplayNode and ASViewController, and allows checking whether a node is in an interface situation where it is prudent to trigger certain diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index bdde67d6ab..aa54b85671 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -110,8 +110,12 @@ static struct ASDisplayNodeFlags GetASDisplayNodeFlags(Class c, ASDisplayNode *i flags.implementsImageDisplay = ([c respondsToSelector:@selector(displayWithParameters:isCancelled:)] ? 1 : 0); if (instance) { flags.implementsDrawParameters = ([instance respondsToSelector:@selector(drawParametersForAsyncLayer:)] ? 1 : 0); + flags.implementsInstanceDrawRect = ([instance respondsToSelector:@selector(drawRect:withParameters:isCancelled:isRasterizing:)] ? 1 : 0); + flags.implementsInstanceImageDisplay = ([instance respondsToSelector:@selector(displayWithParameters:isCancelled:)] ? 1 : 0); } else { flags.implementsDrawParameters = ([c instancesRespondToSelector:@selector(drawParametersForAsyncLayer:)] ? 1 : 0); + flags.implementsInstanceDrawRect = ([c instancesRespondToSelector:@selector(drawRect:withParameters:isCancelled:isRasterizing:)] ? 1 : 0); + flags.implementsInstanceImageDisplay = ([c instancesRespondToSelector:@selector(displayWithParameters:isCancelled:)] ? 1 : 0); } return flags; } @@ -1491,7 +1495,7 @@ static NSInteger incrementIfFound(NSInteger i) { // Helper method to summarize whether or not the node run through the display process - (BOOL)__implementsDisplay { - return _flags.implementsDrawRect == YES || _flags.implementsImageDisplay == YES || self.shouldRasterizeDescendants; + return _flags.implementsDrawRect == YES || _flags.implementsImageDisplay == YES || self.shouldRasterizeDescendants || _flags.implementsInstanceDrawRect || _flags.implementsInstanceImageDisplay; } - (void)_setupPlaceholderLayer diff --git a/AsyncDisplayKit/ASImageNode.mm b/AsyncDisplayKit/ASImageNode.mm index c9024c7510..fda905e0ca 100644 --- a/AsyncDisplayKit/ASImageNode.mm +++ b/AsyncDisplayKit/ASImageNode.mm @@ -14,6 +14,7 @@ #import #import #import +#import #import "ASImageNode+CGExtras.h" @@ -22,42 +23,34 @@ @interface _ASImageNodeDrawParameters : NSObject -@property (nonatomic, assign, readonly) BOOL cropEnabled; @property (nonatomic, assign) BOOL opaque; -@property (nonatomic, retain) UIImage *image; @property (nonatomic, assign) CGRect bounds; @property (nonatomic, assign) CGFloat contentsScale; @property (nonatomic, retain) UIColor *backgroundColor; @property (nonatomic, assign) UIViewContentMode contentMode; -@property (nonatomic, assign) CGRect cropRect; -@property (nonatomic, copy) asimagenode_modification_block_t imageModificationBlock; @end // TODO: eliminate explicit parameters with a set of keys copied from the node @implementation _ASImageNodeDrawParameters -- (id)initWithCrop:(BOOL)cropEnabled opaque:(BOOL)opaque image:(UIImage *)image bounds:(CGRect)bounds contentsScale:(CGFloat)contentsScale backgroundColor:(UIColor *)backgroundColor contentMode:(UIViewContentMode)contentMode cropRect:(CGRect)cropRect imageModificationBlock:(asimagenode_modification_block_t)imageModificationBlock +- (id)initWithBounds:(CGRect)bounds opaque:(BOOL)opaque contentsScale:(CGFloat)contentsScale backgroundColor:(UIColor *)backgroundColor contentMode:(UIViewContentMode)contentMode { self = [self init]; if (!self) return nil; - _cropEnabled = cropEnabled; _opaque = opaque; - _image = image; _bounds = bounds; _contentsScale = contentsScale; _backgroundColor = backgroundColor; _contentMode = contentMode; - _cropRect = cropRect; - _imageModificationBlock = [imageModificationBlock copy]; return self; } - (NSString *)description { - return [NSString stringWithFormat:@"<%@ : %p image:%@ cropEnabled:%@ opaque:%@ bounds:%@ contentsScale:%.2f backgroundColor:%@ contentMode:%@ cropRect:%@>", [self class], self, self.image, @(self.cropEnabled), @(self.opaque), NSStringFromCGRect(self.bounds), self.contentsScale, self.backgroundColor, ASDisplayNodeNSStringFromUIContentMode(self.contentMode), NSStringFromCGRect(self.cropRect)]; + return [NSString stringWithFormat:@"<%@ : %p opaque:%@ bounds:%@ contentsScale:%.2f backgroundColor:%@ contentMode:%@>", [self class], self, @(self.opaque), NSStringFromCGRect(self.bounds), self.contentsScale, self.backgroundColor, ASDisplayNodeNSStringFromUIContentMode(self.contentMode)]; } @end @@ -78,6 +71,7 @@ } @synthesize image = _image; +@synthesize imageModificationBlock = _imageModificationBlock; - (id)init { @@ -153,88 +147,104 @@ { BOOL hasValidCropBounds = _cropEnabled && !CGRectIsNull(_cropDisplayBounds) && !CGRectIsEmpty(_cropDisplayBounds); - return [[_ASImageNodeDrawParameters alloc] initWithCrop:_cropEnabled - opaque:self.opaque - image:self.image - bounds:(hasValidCropBounds ? _cropDisplayBounds : self.bounds) - contentsScale:self.contentsScaleForDisplay - backgroundColor:self.backgroundColor - contentMode:self.contentMode - cropRect:self.cropRect - imageModificationBlock:self.imageModificationBlock]; + return [[_ASImageNodeDrawParameters alloc] initWithBounds:self.bounds + opaque:self.opaque + contentsScale:self.contentsScaleForDisplay + backgroundColor:self.backgroundColor + contentMode:self.contentMode]; } -+ (UIImage *)displayWithParameters:(_ASImageNodeDrawParameters *)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelled +- (UIImage *)displayWithParameters:(_ASImageNodeDrawParameters *)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelled { - UIImage *image = parameters.image; - + ASDN::MutexLocker l(_imageLock); + UIImage *image = _image; if (!image) { return nil; } - - ASDisplayNodeAssert(parameters.contentsScale > 0, @"invalid contentsScale at display time"); - + + BOOL cropEnabled = _cropEnabled; + CGFloat contentsScale = _contentsScaleForDisplay; + CGRect cropDisplayBounds = _cropDisplayBounds; + CGRect cropRect = _cropRect; + asimagenode_modification_block_t imageModificationBlock = _imageModificationBlock; + + ASDN::MutexUnlocker u(_imageLock); + + ASDisplayNodeContextModifier preContextBlock = self.willDisplayNodeContentBlock; + ASDisplayNodeContextModifier postContextBlock = self.didDisplayNodeContentBlock; + + BOOL hasValidCropBounds = cropEnabled && !CGRectIsNull(cropDisplayBounds) && !CGRectIsEmpty(cropDisplayBounds); + + CGRect bounds = (hasValidCropBounds ? cropDisplayBounds : parameters.bounds); + BOOL isOpaque = parameters.opaque; + CGFloat contentsScaleForDisplay = parameters.contentsScale; + UIColor *backgroundColor = parameters.backgroundColor; + UIViewContentMode contentMode = parameters.contentMode; + + ASDisplayNodeAssert(contentsScale > 0, @"invalid contentsScale at display time"); + // if the image is resizable, bail early since the image has likely already been configured BOOL stretchable = !UIEdgeInsetsEqualToEdgeInsets(image.capInsets, UIEdgeInsetsZero); if (stretchable) { - if (parameters.imageModificationBlock != NULL) { - image = parameters.imageModificationBlock(image); + if (imageModificationBlock != NULL) { + image = imageModificationBlock(image); } return image; } - - CGRect bounds = parameters.bounds; - - CGFloat contentsScale = parameters.contentsScale; - UIViewContentMode contentMode = parameters.contentMode; + CGSize imageSize = image.size; CGSize imageSizeInPixels = CGSizeMake(imageSize.width * image.scale, imageSize.height * image.scale); CGSize boundsSizeInPixels = CGSizeMake(floorf(bounds.size.width * contentsScale), floorf(bounds.size.height * contentsScale)); - + BOOL contentModeSupported = contentMode == UIViewContentModeScaleAspectFill - || contentMode == UIViewContentModeScaleAspectFit - || contentMode == UIViewContentModeCenter; - + || contentMode == UIViewContentModeScaleAspectFit + || contentMode == UIViewContentModeCenter; + CGSize backingSize; CGRect imageDrawRect; - + if (boundsSizeInPixels.width * contentsScale < 1.0f || boundsSizeInPixels.height * contentsScale < 1.0f || imageSizeInPixels.width < 1.0f || imageSizeInPixels.height < 1.0f) { return nil; } - + // If we're not supposed to do any cropping, just decode image at original size - if (!parameters.cropEnabled || !contentModeSupported || stretchable) { + if (!cropEnabled || !contentModeSupported || stretchable) { backingSize = imageSizeInPixels; imageDrawRect = (CGRect){.size = backingSize}; } else { ASCroppedImageBackingSizeAndDrawRectInBounds(imageSizeInPixels, boundsSizeInPixels, contentMode, - parameters.cropRect, + cropRect, &backingSize, &imageDrawRect); } - + if (backingSize.width <= 0.0f || backingSize.height <= 0.0f || imageDrawRect.size.width <= 0.0f || imageDrawRect.size.height <= 0.0f) { return nil; } - + // Use contentsScale of 1.0 and do the contentsScale handling in boundsSizeInPixels so ASCroppedImageBackingSizeAndDrawRectInBounds // will do its rounding on pixel instead of point boundaries - UIGraphicsBeginImageContextWithOptions(backingSize, parameters.opaque, 1.0); + UIGraphicsBeginImageContextWithOptions(backingSize, isOpaque, 1.0); // if view is opaque, fill the context with background color - if (parameters.opaque && parameters.backgroundColor) { - [parameters.backgroundColor setFill]; + if (isOpaque && backgroundColor) { + [backgroundColor setFill]; UIRectFill({ .size = backingSize }); } - + + CGContextRef context = UIGraphicsGetCurrentContext(); + if (preContextBlock) { + preContextBlock(context); + } + // iOS 9 appears to contain a thread safety regression when drawing the same CGImageRef on // multiple threads concurrently. In fact, instead of crashing, it appears to deadlock. // The issue is present in Mac OS X El Capitan and has been seen hanging Pro apps like Adobe Premier, @@ -250,20 +260,24 @@ @synchronized(image) { [image drawInRect:imageDrawRect]; } - + + if (postContextBlock) { + postContextBlock(context); + } + if (isCancelled()) { UIGraphicsEndImageContext(); return nil; } - + UIImage *result = UIGraphicsGetImageFromCurrentImageContext(); - + UIGraphicsEndImageContext(); - - if (parameters.imageModificationBlock != NULL) { - result = parameters.imageModificationBlock(result); + + if (imageModificationBlock != NULL) { + result = imageModificationBlock(result); } - + return result; } @@ -271,8 +285,9 @@ { [super displayDidFinish]; + ASDN::MutexLocker l(_imageLock); // If we've got a block to perform after displaying, do it. - if (self.image && _displayCompletionBlock) { + if (_image && _displayCompletionBlock) { // FIXME: _displayCompletionBlock is not protected by lock _displayCompletionBlock(NO); @@ -291,6 +306,7 @@ // Stash the block and call-site queue. We'll invoke it in -displayDidFinish. // FIXME: _displayCompletionBlock not protected by lock + ASDN::MutexLocker l(_imageLock); if (_displayCompletionBlock != displayCompletionBlock) { _displayCompletionBlock = [displayCompletionBlock copy]; } @@ -301,6 +317,7 @@ #pragma mark - Cropping - (BOOL)isCropEnabled { + ASDN::MutexLocker l(_imageLock); return _cropEnabled; } @@ -311,6 +328,7 @@ - (void)setCropEnabled:(BOOL)cropEnabled recropImmediately:(BOOL)recropImmediately inBounds:(CGRect)cropBounds { + ASDN::MutexLocker l(_imageLock); if (_cropEnabled == cropEnabled) return; @@ -331,11 +349,13 @@ - (CGRect)cropRect { + ASDN::MutexLocker l(_imageLock); return _cropRect; } - (void)setCropRect:(CGRect)cropRect { + ASDN::MutexLocker l(_imageLock); if (CGRectEqualToRect(_cropRect, cropRect)) return; @@ -354,6 +374,18 @@ }); } +- (asimagenode_modification_block_t)imageModificationBlock +{ + ASDN::MutexLocker l(_imageLock); + return _imageModificationBlock; +} + +- (void)setImageModificationBlock:(asimagenode_modification_block_t)imageModificationBlock +{ + ASDN::MutexLocker l(_imageLock); + _imageModificationBlock = imageModificationBlock; +} + @end diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index 8dbea6eb57..2741dda576 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -34,13 +34,7 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation @interface ASTextNodeDrawParameters : NSObject -- (instancetype)initWithRenderer:(ASTextKitRenderer *)renderer - textOrigin:(CGPoint)textOrigin - backgroundColor:(UIColor *)backgroundColor; - -@property (nonatomic, strong, readonly) ASTextKitRenderer *renderer; - -@property (nonatomic, assign, readonly) CGPoint textOrigin; +@property (nonatomic, assign, readonly) CGRect bounds; @property (nonatomic, strong, readonly) UIColor *backgroundColor; @@ -48,30 +42,16 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation @implementation ASTextNodeDrawParameters -- (instancetype)initWithRenderer:(ASTextKitRenderer *)renderer - textOrigin:(CGPoint)textOrigin - backgroundColor:(UIColor *)backgroundColor +- (instancetype)initWithBounds:(CGRect)bounds + backgroundColor:(UIColor *)backgroundColor { if (self = [super init]) { - _renderer = renderer; - _textOrigin = textOrigin; + _bounds = bounds; _backgroundColor = backgroundColor; } return self; } -- (void)dealloc -{ - // Destruction of the layout managers/containers/text storage is quite - // expensive, and can take some time, so we dispatch onto a bg queue to - // actually dealloc. - __block ASTextKitRenderer *renderer = _renderer; - ASPerformBlockOnBackgroundThread(^{ - renderer = nil; - }); - _renderer = nil; -} - @end @interface ASTextNode () @@ -237,11 +217,17 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; #pragma mark - Renderer Management +//only safe to call on the main thread - (ASTextKitRenderer *)_renderer +{ + return [self _rendererWithBounds:self.bounds]; +} + +- (ASTextKitRenderer *)_rendererWithBounds:(CGRect)bounds { ASDN::MutexLocker l(_rendererLock); if (_renderer == nil) { - CGSize constrainedSize = _constrainedSize.width != -INFINITY ? _constrainedSize : self.bounds.size; + CGSize constrainedSize = _constrainedSize.width != -INFINITY ? _constrainedSize : bounds.size; _renderer = [[ASTextKitRenderer alloc] initWithTextKitAttributes:[self _rendererAttributes] constrainedSize:constrainedSize]; } @@ -420,13 +406,17 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; #pragma mark - Drawing -+ (void)drawRect:(CGRect)bounds withParameters:(ASTextNodeDrawParameters *)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing +- (void)drawRect:(CGRect)bounds withParameters:(ASTextNodeDrawParameters *)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing { CGContextRef context = UIGraphicsGetCurrentContext(); ASDisplayNodeAssert(context, @"This is no good without a context."); - + CGContextSaveGState(context); - + + ASTextKitRenderer *renderer = [self _rendererWithBounds:parameters.bounds]; + UIEdgeInsets shadowPadding = [self shadowPaddingWithRenderer:renderer]; + CGPoint textOrigin = CGPointMake(parameters.bounds.origin.x - shadowPadding.left, parameters.bounds.origin.y - shadowPadding.top); + // Fill background if (!isRasterizing) { UIColor *backgroundColor = parameters.backgroundColor; @@ -435,28 +425,20 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; UIRectFillUsingBlendMode(CGContextGetClipBoundingBox(context), kCGBlendModeCopy); } } - + // Draw shadow - [[parameters.renderer shadower] setShadowInContext:context]; - + [[renderer shadower] setShadowInContext:context]; + // Draw text - bounds.origin = parameters.textOrigin; - [parameters.renderer drawInContext:context bounds:bounds]; - + bounds.origin = textOrigin; + [renderer drawInContext:context bounds:bounds]; + CGContextRestoreGState(context); } - (NSObject *)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer { - CGRect bounds = self.bounds; - [self _invalidateRendererIfNeededForBoundsSize:bounds.size]; - - // Offset the text origin by any shadow padding - UIEdgeInsets shadowPadding = [self shadowPadding]; - CGPoint textOrigin = CGPointMake(bounds.origin.x - shadowPadding.left, bounds.origin.y - shadowPadding.top); - return [[ASTextNodeDrawParameters alloc] initWithRenderer:[self _renderer] - textOrigin:textOrigin - backgroundColor:self.backgroundColor]; + return [[ASTextNodeDrawParameters alloc] initWithBounds:self.bounds backgroundColor:self.backgroundColor]; } #pragma mark - Attributes @@ -1016,9 +998,15 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; } } +//only safe to call on main thread - (UIEdgeInsets)shadowPadding { - return [self _renderer].shadower.shadowPadding; + return [self shadowPaddingWithRenderer:[self _renderer]]; +} + +- (UIEdgeInsets)shadowPaddingWithRenderer:(ASTextKitRenderer *)renderer +{ + return renderer.shadower.shadowPadding; } #pragma mark - Truncation Message diff --git a/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm b/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm index a642275305..e60257114e 100644 --- a/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm +++ b/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm @@ -12,6 +12,7 @@ #import "ASAssert.h" #import "ASDisplayNodeInternal.h" #import "ASDisplayNode+FrameworkPrivate.h" +#import "ASDisplayNode+Beta.h" @interface ASDisplayNode () <_ASDisplayLayerDelegate> @end @@ -224,25 +225,30 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync, return image; }; - } else if (_flags.implementsImageDisplay) { + } else if (_flags.implementsInstanceImageDisplay || _flags.implementsImageDisplay) { // Capture drawParameters from delegate on main thread id drawParameters = [self drawParameters]; - + displayBlock = ^id{ __ASDisplayLayerIncrementConcurrentDisplayCount(asynchronous, rasterizing); if (isCancelledBlock()) { __ASDisplayLayerDecrementConcurrentDisplayCount(asynchronous, rasterizing); return nil; } - + ASDN_DELAY_FOR_DISPLAY(); - - UIImage *result = [[self class] displayWithParameters:drawParameters isCancelled:isCancelledBlock]; + + UIImage *result = nil; + if (_flags.implementsInstanceImageDisplay) { + result = [self displayWithParameters:drawParameters isCancelled:isCancelledBlock]; + } else { + result = [[self class] displayWithParameters:drawParameters isCancelled:isCancelledBlock]; + } __ASDisplayLayerDecrementConcurrentDisplayCount(asynchronous, rasterizing); return result; }; - - } else if (_flags.implementsDrawRect) { + + } else if (_flags.implementsInstanceDrawRect || _flags.implementsDrawRect) { CGRect bounds = self.bounds; if (CGRectIsEmpty(bounds)) { @@ -267,7 +273,20 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync, UIGraphicsBeginImageContextWithOptions(bounds.size, opaque, contentsScaleForDisplay); } - [[self class] drawRect:bounds withParameters:drawParameters isCancelled:isCancelledBlock isRasterizing:rasterizing]; + CGContextRef currentContext = UIGraphicsGetCurrentContext(); + if (_preContextModifier) { + _preContextModifier(currentContext); + } + + if (_flags.implementsInstanceDrawRect) { + [self drawRect:bounds withParameters:drawParameters isCancelled:isCancelledBlock isRasterizing:rasterizing]; + } else { + [[self class] drawRect:bounds withParameters:drawParameters isCancelled:isCancelledBlock isRasterizing:rasterizing]; + } + + if (_postContextModifier) { + _postContextModifier(currentContext); + } if (isCancelledBlock()) { if (!rasterizing) { @@ -370,4 +389,28 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync, [_displaySentinel increment]; } +- (ASDisplayNodeContextModifier)willDisplayNodeContentBlock +{ + ASDN::MutexLocker l(_propertyLock); + return _preContextModifier; +} + +- (ASDisplayNodeContextModifier)didDisplayNodeContentBlock +{ + ASDN::MutexLocker l(_propertyLock); + return _postContextModifier; +} + +- (void)setWillDisplayNodeContentBlock:(ASDisplayNodeContextModifier)contextModifier +{ + ASDN::MutexLocker l(_propertyLock); + _preContextModifier = contextModifier; +} + +- (void)setDidDisplayNodeContentBlock:(ASDisplayNodeContextModifier)contextModifier; +{ + ASDN::MutexLocker l(_propertyLock); + _postContextModifier = contextModifier; +} + @end diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index 4bac260403..a1864518ce 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -87,7 +87,9 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) unsigned hasCustomDrawingPriority:1; // whether custom drawing is enabled + unsigned implementsInstanceDrawRect:1; unsigned implementsDrawRect:1; + unsigned implementsInstanceImageDisplay:1; unsigned implementsImageDisplay:1; unsigned implementsDrawParameters:1; @@ -101,6 +103,9 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) ASDisplayNodeExtraIvars _extra; + ASDisplayNodeContextModifier _preContextModifier; + ASDisplayNodeContextModifier _postContextModifier; + #if TIME_DISPLAYNODE_OPS @public NSTimeInterval _debugTimeToCreateView;