From af932e7596a78180df09d67b2ac2b69236445697 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 8 Jun 2016 14:39:54 -0700 Subject: [PATCH 1/5] Simplify ASLayout flatten BFS --- AsyncDisplayKit/Layout/ASLayout.mm | 40 ++++++++++++++---------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/AsyncDisplayKit/Layout/ASLayout.mm b/AsyncDisplayKit/Layout/ASLayout.mm index 007e92fa00..38d8e0fc61 100644 --- a/AsyncDisplayKit/Layout/ASLayout.mm +++ b/AsyncDisplayKit/Layout/ASLayout.mm @@ -113,36 +113,32 @@ extern BOOL CGPointIsNull(CGPoint point) struct Context { ASLayout *layout; - CGPoint absolutePosition; - BOOL visited; + CGPoint relativePosition; BOOL flattened; }; // Queue used to keep track of sublayouts while traversing this layout in a BFS fashion. std::queue queue; - queue.push({self, CGPointMake(0, 0), NO, NO}); + queue.push({self, CGPointMake(0, 0), NO}); while (!queue.empty()) { Context &context = queue.front(); - if (context.visited) { - queue.pop(); - } else { - context.visited = YES; - - if (predicateBlock(context.layout)) { - [flattenedSublayouts addObject:[ASLayout layoutWithLayoutableObject:context.layout.layoutableObject - constrainedSizeRange:context.layout.constrainedSizeRange - size:context.layout.size - position:context.absolutePosition - sublayouts:nil - flattened:context.flattened]]; - } - - for (ASLayout *sublayout in context.layout.sublayouts) { - // Mark layout trees that have already been flattened for future identification of immediate sublayouts - BOOL flattened = context.flattened ? : context.layout.flattened; - queue.push({sublayout, context.absolutePosition + sublayout.position, NO, flattened}); - } + ASLayout *layout = context.layout; + queue.pop(); + + if (predicateBlock(layout)) { + [flattenedSublayouts addObject:[ASLayout layoutWithLayoutableObject:layout.layoutableObject + constrainedSizeRange:layout.constrainedSizeRange + size:layout.size + position:context.relativePosition + sublayouts:nil + flattened:context.flattened]]; + } + + for (ASLayout *sublayout in layout.sublayouts) { + // Mark layout trees that have already been flattened for future identification of immediate sublayouts + BOOL flattened = context.flattened ? : layout.flattened; + queue.push({sublayout, context.relativePosition + sublayout.position, flattened}); } } From f1f45c61a8980eee2f935c62d0d23161554cf4d0 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 8 Jun 2016 14:51:20 -0700 Subject: [PATCH 2/5] Copy dequeued context --- AsyncDisplayKit/Layout/ASLayout.mm | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/AsyncDisplayKit/Layout/ASLayout.mm b/AsyncDisplayKit/Layout/ASLayout.mm index 38d8e0fc61..d715e45a48 100644 --- a/AsyncDisplayKit/Layout/ASLayout.mm +++ b/AsyncDisplayKit/Layout/ASLayout.mm @@ -122,22 +122,21 @@ extern BOOL CGPointIsNull(CGPoint point) queue.push({self, CGPointMake(0, 0), NO}); while (!queue.empty()) { - Context &context = queue.front(); - ASLayout *layout = context.layout; + Context context = queue.front(); queue.pop(); - if (predicateBlock(layout)) { - [flattenedSublayouts addObject:[ASLayout layoutWithLayoutableObject:layout.layoutableObject - constrainedSizeRange:layout.constrainedSizeRange - size:layout.size + if (predicateBlock(context.layout)) { + [flattenedSublayouts addObject:[ASLayout layoutWithLayoutableObject:context.layout.layoutableObject + constrainedSizeRange:context.layout.constrainedSizeRange + size:context.layout.size position:context.relativePosition sublayouts:nil flattened:context.flattened]]; } - for (ASLayout *sublayout in layout.sublayouts) { + for (ASLayout *sublayout in context.layout.sublayouts) { // Mark layout trees that have already been flattened for future identification of immediate sublayouts - BOOL flattened = context.flattened ? : layout.flattened; + BOOL flattened = context.flattened ? : context.layout.flattened; queue.push({sublayout, context.relativePosition + sublayout.position, flattened}); } } From 4149c6b0f59547734984780dfc4dceaac5f53bbc Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 8 Jun 2016 15:29:47 -0700 Subject: [PATCH 3/5] Move ASLayout frame to a computed property --- AsyncDisplayKit/Layout/ASLayout.h | 12 ++++++------ AsyncDisplayKit/Layout/ASLayout.mm | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/AsyncDisplayKit/Layout/ASLayout.h b/AsyncDisplayKit/Layout/ASLayout.h index 7733a6a207..0d0bcfa281 100644 --- a/AsyncDisplayKit/Layout/ASLayout.h +++ b/AsyncDisplayKit/Layout/ASLayout.h @@ -68,6 +68,12 @@ extern BOOL CGPointIsNull(CGPoint point); */ @property (nonatomic, readonly, getter=isFlattened) BOOL flattened; +/** + * @abstract Returns a valid frame for the current layout computed with the size and position. + * @discussion Clamps the layout's origin or position to 0 if any of the calculated values are infinite. + */ +@property (nonatomic, readonly) CGRect frame; + /** * Initializer. * @@ -142,12 +148,6 @@ extern BOOL CGPointIsNull(CGPoint point); */ - (ASLayout *)flattenedLayoutUsingPredicateBlock:(BOOL (^)(ASLayout *evaluatedLayout))predicateBlock; -/** - * @abstract Returns a valid frame for the current layout computed with the size and position. - * @discussion Clamps the layout's origin or position to 0 if any of the calculated values are infinite. - */ -- (CGRect)frame; - @end NS_ASSUME_NONNULL_END diff --git a/AsyncDisplayKit/Layout/ASLayout.mm b/AsyncDisplayKit/Layout/ASLayout.mm index d715e45a48..e5f0cd1778 100644 --- a/AsyncDisplayKit/Layout/ASLayout.mm +++ b/AsyncDisplayKit/Layout/ASLayout.mm @@ -24,6 +24,8 @@ extern BOOL CGPointIsNull(CGPoint point) @implementation ASLayout +@dynamic frame; + + (instancetype)layoutWithLayoutableObject:(id)layoutableObject constrainedSizeRange:(ASSizeRange)sizeRange size:(CGSize)size From a3e8f556a3259aabd4ac1420508722419d5b6fdd Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 8 Jun 2016 17:55:20 -0700 Subject: [PATCH 4/5] Clean up flattening process in ASLayout --- AsyncDisplayKit/ASDisplayNode.mm | 10 +- AsyncDisplayKit/Layout/ASLayout.h | 56 +++---- AsyncDisplayKit/Layout/ASLayout.mm | 137 +++++++++++------- AsyncDisplayKit/Private/ASLayoutTransition.mm | 6 +- AsyncDisplayKit/_ASTransitionContext.m | 2 +- .../ASLayoutSpecSnapshotTestsHelper.m | 4 +- 6 files changed, 113 insertions(+), 102 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 7f59ef65df..290abcc86c 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -1916,13 +1916,7 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) ASLayoutableValidateLayout(layout); #endif } - return [layout flattenedLayoutUsingPredicateBlock:^BOOL(ASLayout *evaluatedLayout) { - if (self.usesImplicitHierarchyManagement) { - return ASObjectIsEqual(layout, evaluatedLayout) == NO && [evaluatedLayout.layoutableObject isKindOfClass:[ASDisplayNode class]]; - } else { - return [_subnodes containsObject:evaluatedLayout.layoutableObject]; - } - }]; + return [layout filteredNodeLayoutTree]; } else { // If neither -layoutSpecThatFits: nor -calculateSizeThatFits: is overridden by subclassses, preferredFrameSize should be used, // assume that the default implementation of -calculateSizeThatFits: returns it. @@ -2390,7 +2384,7 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) - (void)__layoutSublayouts { - for (ASLayout *subnodeLayout in _layout.immediateSublayouts) { + for (ASLayout *subnodeLayout in _layout.sublayouts) { ((ASDisplayNode *)subnodeLayout.layoutableObject).frame = [subnodeLayout frame]; } } diff --git a/AsyncDisplayKit/Layout/ASLayout.h b/AsyncDisplayKit/Layout/ASLayout.h index 0d0bcfa281..a0e190c761 100644 --- a/AsyncDisplayKit/Layout/ASLayout.h +++ b/AsyncDisplayKit/Layout/ASLayout.h @@ -53,21 +53,11 @@ extern BOOL CGPointIsNull(CGPoint point); */ @property (nonatomic, readonly) NSArray *sublayouts; -/** - * A list of sublayouts that were not already flattened. - */ -@property (nonatomic, readonly) NSArray *immediateSublayouts; - /** * Mark the layout dirty for future regeneration. */ @property (nonatomic, getter=isDirty) BOOL dirty; -/** - * A boolean describing if the current layout has been flattened. - */ -@property (nonatomic, readonly, getter=isFlattened) BOOL flattened; - /** * @abstract Returns a valid frame for the current layout computed with the size and position. * @discussion Clamps the layout's origin or position to 0 if any of the calculated values are infinite. @@ -75,22 +65,27 @@ extern BOOL CGPointIsNull(CGPoint point); @property (nonatomic, readonly) CGRect frame; /** - * Initializer. + * Designated initializer + */ +- (instancetype)initWithLayoutableObject:(id)layoutableObject + constrainedSizeRange:(ASSizeRange)sizeRange + size:(CGSize)size + position:(CGPoint)position + sublayouts:(NSArray *)sublayouts NS_DESIGNATED_INITIALIZER; + +/** + * Convenience class initializer for layout construction. * * @param layoutableObject The backing ASLayoutable object. - * - * @param size The size of this layout. - * - * @param position The position of this layout within its parent (if available). - * - * @param sublayouts Sublayouts belong to the new layout. + * @param size The size of this layout. + * @param position The position of this layout within its parent (if available). + * @param sublayouts Sublayouts belong to the new layout. */ + (instancetype)layoutWithLayoutableObject:(id)layoutableObject constrainedSizeRange:(ASSizeRange)sizeRange size:(CGSize)size position:(CGPoint)position - sublayouts:(nullable NSArray *)sublayouts - flattened:(BOOL)flattened; + sublayouts:(nullable NSArray *)sublayouts; /** * Convenience initializer that has CGPointNull position. @@ -99,9 +94,7 @@ extern BOOL CGPointIsNull(CGPoint point); * or for creating a sublayout of which the position is yet to be determined. * * @param layoutableObject The backing ASLayoutable object. - * * @param size The size of this layout. - * * @param sublayouts Sublayouts belong to the new layout. */ + (instancetype)layoutWithLayoutableObject:(id)layoutableObject @@ -115,7 +108,6 @@ extern BOOL CGPointIsNull(CGPoint point); * or a sublayout of which the position is yet to be determined. * * @param layoutableObject The backing ASLayoutable object. - * * @param size The size of this layout. */ + (instancetype)layoutWithLayoutableObject:(id)layoutableObject @@ -126,9 +118,7 @@ extern BOOL CGPointIsNull(CGPoint point); * Convenience initializer that is flattened and has CGPointNull position. * * @param layoutableObject The backing ASLayoutable object. - * * @param size The size of this layout. - * * @param sublayouts Sublayouts belong to the new layout. */ + (instancetype)flattenedLayoutWithLayoutableObject:(id)layoutableObject @@ -137,16 +127,16 @@ extern BOOL CGPointIsNull(CGPoint point); sublayouts:(nullable NSArray *)sublayouts; /** - * @abstract Evaluates a given predicate block against each object in the receiving layout tree - * and returns a new, 1-level deep layout containing the objects for which the predicate block returns true. - * - * @param predicateBlock The block is applied to a layout to be evaluated. - * The block takes 1 argument: evaluatedLayout - the layout to be evaluated. - * The block returns YES if evaluatedLayout evaluates to true, otherwise NO. - * - * @return A new, 1-level deep layout containing the layouts for which the predicate block returns true. + * Convenience initializer that creates a layout based on the values of the given layout, with a new position + * @param layout The layout to use to create the new layout + * @param position The position of the new layout */ -- (ASLayout *)flattenedLayoutUsingPredicateBlock:(BOOL (^)(ASLayout *evaluatedLayout))predicateBlock; ++ (instancetype)layoutWithLayout:(ASLayout *)layout position:(CGPoint)position; + +/** + * Traverses the existing layout tree and generates a new tree that represents only ASDisplayNode layouts + */ +- (ASLayout *)filteredNodeLayoutTree; @end diff --git a/AsyncDisplayKit/Layout/ASLayout.mm b/AsyncDisplayKit/Layout/ASLayout.mm index e5f0cd1778..56d270b164 100644 --- a/AsyncDisplayKit/Layout/ASLayout.mm +++ b/AsyncDisplayKit/Layout/ASLayout.mm @@ -9,10 +9,13 @@ */ #import "ASLayout.h" + #import "ASAssert.h" -#import "ASLayoutSpecUtilities.h" -#import "ASInternalHelpers.h" #import "ASDimension.h" +#import "ASDisplayNode.h" +#import "ASInternalHelpers.h" +#import "ASLayoutSpecUtilities.h" + #import CGPoint const CGPointNull = {NAN, NAN}; @@ -22,27 +25,35 @@ extern BOOL CGPointIsNull(CGPoint point) return isnan(point.x) && isnan(point.y); } +@interface ASLayout () + +/** + * A boolean describing if the current layout has been flattened. + */ +@property (nonatomic, getter=isFlattened) BOOL flattened; + +@end + @implementation ASLayout @dynamic frame; -+ (instancetype)layoutWithLayoutableObject:(id)layoutableObject - constrainedSizeRange:(ASSizeRange)sizeRange - size:(CGSize)size - position:(CGPoint)position - sublayouts:(NSArray *)sublayouts - flattened:(BOOL)flattened +- (instancetype)initWithLayoutableObject:(id)layoutableObject + constrainedSizeRange:(ASSizeRange)sizeRange + size:(CGSize)size + position:(CGPoint)position + sublayouts:(NSArray *)sublayouts { - ASDisplayNodeAssert(layoutableObject, @"layoutableObject is required."); + self = [super init]; + if (self) { + NSParameterAssert(layoutableObject); #if DEBUG - for (ASLayout *sublayout in sublayouts) { - ASDisplayNodeAssert(!CGPointIsNull(sublayout.position), @"Invalid position is not allowed in sublayout."); - } + for (ASLayout *sublayout in sublayouts) { + ASDisplayNodeAssert(CGPointIsNull(sublayout.position) == NO, @"Invalid position is not allowed in sublayout."); + } #endif - - ASLayout *l = [super new]; - if (l) { - l->_layoutableObject = layoutableObject; + + _layoutableObject = layoutableObject; if (!isValidForLayout(size.width) || !isValidForLayout(size.height)) { ASDisplayNodeAssert(NO, @"layoutSize is invalid and unsafe to provide to Core Animation! Production will force to 0, 0. Size = %@, node = %@", NSStringFromCGSize(size), layoutableObject); @@ -50,27 +61,40 @@ extern BOOL CGPointIsNull(CGPoint point) } else { size = CGSizeMake(ASCeilPixelValue(size.width), ASCeilPixelValue(size.height)); } - l->_constrainedSizeRange = sizeRange; - l->_size = size; - l->_dirty = NO; + _constrainedSizeRange = sizeRange; + _size = size; + _dirty = NO; if (CGPointIsNull(position) == NO) { - l->_position = CGPointMake(ASCeilPixelValue(position.x), ASCeilPixelValue(position.y)); + _position = CGPointMake(ASCeilPixelValue(position.x), ASCeilPixelValue(position.y)); } else { - l->_position = position; + _position = position; } - l->_sublayouts = [sublayouts copy]; - l->_flattened = flattened; - - NSMutableArray *result = [NSMutableArray array]; - for (ASLayout *sublayout in l->_sublayouts) { - if (!sublayout.isFlattened) { - [result addObject:sublayout]; - } - } - l->_immediateSublayouts = result; + _sublayouts = sublayouts != nil ? [sublayouts copy] : @[]; + _flattened = NO; } - return l; + return self; +} + +- (instancetype)init +{ + ASDisplayNodeAssert(NO, @"Use the designated initializer"); + return [self init]; +} + +#pragma mark - Class Constructors + ++ (instancetype)layoutWithLayoutableObject:(id)layoutableObject + constrainedSizeRange:(ASSizeRange)sizeRange + size:(CGSize)size + position:(CGPoint)position + sublayouts:(NSArray *)sublayouts +{ + return [[self alloc] initWithLayoutableObject:layoutableObject + constrainedSizeRange:sizeRange + size:size + position:position + sublayouts:sublayouts]; } + (instancetype)layoutWithLayoutableObject:(id)layoutableObject @@ -82,8 +106,7 @@ extern BOOL CGPointIsNull(CGPoint point) constrainedSizeRange:sizeRange size:size position:CGPointNull - sublayouts:sublayouts - flattened:NO]; + sublayouts:sublayouts]; } + (instancetype)layoutWithLayoutableObject:(id)layoutableObject @@ -105,48 +128,54 @@ extern BOOL CGPointIsNull(CGPoint point) constrainedSizeRange:sizeRange size:size position:CGPointNull - sublayouts:sublayouts - flattened:YES]; + sublayouts:sublayouts]; } -- (ASLayout *)flattenedLayoutUsingPredicateBlock:(BOOL (^)(ASLayout *))predicateBlock ++ (instancetype)layoutWithLayout:(ASLayout *)layout position:(CGPoint)position +{ + return [self layoutWithLayoutableObject:layout.layoutableObject + constrainedSizeRange:layout.constrainedSizeRange + size:layout.size + position:position + sublayouts:layout.sublayouts]; +} + +#pragma mark - Layout Flattening + +- (ASLayout *)filteredNodeLayoutTree { NSMutableArray *flattenedSublayouts = [NSMutableArray array]; struct Context { ASLayout *layout; - CGPoint relativePosition; - BOOL flattened; + CGPoint absolutePosition; }; // Queue used to keep track of sublayouts while traversing this layout in a BFS fashion. std::queue queue; - queue.push({self, CGPointMake(0, 0), NO}); + queue.push({self, CGPointMake(0, 0)}); while (!queue.empty()) { Context context = queue.front(); queue.pop(); - if (predicateBlock(context.layout)) { - [flattenedSublayouts addObject:[ASLayout layoutWithLayoutableObject:context.layout.layoutableObject - constrainedSizeRange:context.layout.constrainedSizeRange - size:context.layout.size - position:context.relativePosition - sublayouts:nil - flattened:context.flattened]]; + if (self != context.layout && [context.layout.layoutableObject isKindOfClass:[ASDisplayNode class]]) { + ASLayout *layout = [ASLayout layoutWithLayout:context.layout position:context.absolutePosition]; + layout.flattened = YES; + [flattenedSublayouts addObject:layout]; } for (ASLayout *sublayout in context.layout.sublayouts) { - // Mark layout trees that have already been flattened for future identification of immediate sublayouts - BOOL flattened = context.flattened ? : context.layout.flattened; - queue.push({sublayout, context.relativePosition + sublayout.position, flattened}); + if (sublayout.isFlattened == NO) { + queue.push({sublayout, context.absolutePosition + sublayout.position}); + } } } - return [ASLayout flattenedLayoutWithLayoutableObject:_layoutableObject - constrainedSizeRange:_constrainedSizeRange - size:_size - sublayouts:flattenedSublayouts]; + return [ASLayout layoutWithLayoutableObject:_layoutableObject + constrainedSizeRange:_constrainedSizeRange + size:_size + sublayouts:flattenedSublayouts]; } - (CGRect)frame diff --git a/AsyncDisplayKit/Private/ASLayoutTransition.mm b/AsyncDisplayKit/Private/ASLayoutTransition.mm index af85fa8cf3..142fbb6cb0 100644 --- a/AsyncDisplayKit/Private/ASLayoutTransition.mm +++ b/AsyncDisplayKit/Private/ASLayoutTransition.mm @@ -67,7 +67,7 @@ } if (_previousLayout) { NSIndexSet *insertions, *deletions; - [_previousLayout.immediateSublayouts asdk_diffWithArray:_pendingLayout.immediateSublayouts + [_previousLayout.sublayouts asdk_diffWithArray:_pendingLayout.sublayouts insertions:&insertions deletions:&deletions compareBlock:^BOOL(ASLayout *lhs, ASLayout *rhs) { @@ -80,7 +80,7 @@ &_removedSubnodes, &_removedSubnodePositions); } else { - NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [_pendingLayout.immediateSublayouts count])]; + NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [_pendingLayout.sublayouts count])]; findNodesInLayoutAtIndexes(_pendingLayout, indexes, &_insertedSubnodes, &_insertedSubnodePositions); _removedSubnodes = nil; } @@ -160,7 +160,7 @@ static inline void findNodesInLayoutAtIndexesWithFilteredNodes(ASLayout *layout, std::vector positions = std::vector(); NSUInteger idx = [indexes firstIndex]; while (idx != NSNotFound) { - ASDisplayNode *node = (ASDisplayNode *)layout.immediateSublayouts[idx].layoutableObject; + ASDisplayNode *node = (ASDisplayNode *)layout.sublayouts[idx].layoutableObject; ASDisplayNodeCAssert(node, @"A flattened layout must consist exclusively of node sublayouts"); // Ignore the odd case in which a non-node sublayout is accessed and the type cast fails if (node != nil) { diff --git a/AsyncDisplayKit/_ASTransitionContext.m b/AsyncDisplayKit/_ASTransitionContext.m index 2474f3f395..d16db9b6eb 100644 --- a/AsyncDisplayKit/_ASTransitionContext.m +++ b/AsyncDisplayKit/_ASTransitionContext.m @@ -71,7 +71,7 @@ NSString * const ASTransitionContextToLayoutKey = @"org.asyncdisplaykit.ASTransi - (NSArray *)subnodesForKey:(NSString *)key { NSMutableArray *subnodes = [NSMutableArray array]; - for (ASLayout *sublayout in [self layoutForKey:key].immediateSublayouts) { + for (ASLayout *sublayout in [self layoutForKey:key].sublayouts) { [subnodes addObject:(ASDisplayNode *)sublayout.layoutableObject]; } return subnodes; diff --git a/AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.m b/AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.m index 0e32958e54..3c7da308bb 100644 --- a/AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.m +++ b/AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.m @@ -65,9 +65,7 @@ constrainedSizeRange:sizeRange size:layout.size sublayouts:@[layout]]; - _layoutUnderTest = [layout flattenedLayoutUsingPredicateBlock:^BOOL(ASLayout *evaluatedLayout) { - return [self.subnodes containsObject:(ASDisplayNode *)evaluatedLayout.layoutableObject]; - }]; + _layoutUnderTest = [layout filteredNodeLayoutTree]; self.frame = CGRectMake(0, 0, _layoutUnderTest.size.width, _layoutUnderTest.size.height); [self measure:_layoutUnderTest.size]; } From 7e8d519a9ad8041e39cb4e752e1e5f7612faeef3 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 8 Jun 2016 18:28:21 -0700 Subject: [PATCH 5/5] Introduce type property to replace layout id check --- AsyncDisplayKit/ASDisplayNode.mm | 9 ++++++++- AsyncDisplayKit/Layout/ASLayout.h | 5 +++++ AsyncDisplayKit/Layout/ASLayout.mm | 12 +++++++++--- AsyncDisplayKit/Layout/ASLayoutSpec.mm | 8 +++++++- AsyncDisplayKit/Layout/ASLayoutable.h | 8 +++++++- 5 files changed, 36 insertions(+), 6 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 290abcc86c..2ddfcf185f 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -62,7 +62,9 @@ NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"AS @implementation ASDisplayNode // these dynamic properties all defined in ASLayoutOptionsPrivate.m -@dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis, alignSelf, ascender, descender, sizeRange, layoutPosition; +@dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis, + alignSelf, ascender, descender, sizeRange, layoutPosition, layoutableType; + @synthesize name = _name; @synthesize preferredFrameSize = _preferredFrameSize; @synthesize isFinalLayoutable = _isFinalLayoutable; @@ -659,6 +661,11 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) return _layout == nil || _layout.isDirty; } +- (ASLayoutableType)layoutableType +{ + return ASLayoutableTypeDisplayNode; +} + #pragma mark - Layout Transition - (void)transitionLayoutWithAnimation:(BOOL)animated diff --git a/AsyncDisplayKit/Layout/ASLayout.h b/AsyncDisplayKit/Layout/ASLayout.h index a0e190c761..60acb5f218 100644 --- a/AsyncDisplayKit/Layout/ASLayout.h +++ b/AsyncDisplayKit/Layout/ASLayout.h @@ -31,6 +31,11 @@ extern BOOL CGPointIsNull(CGPoint point); */ @property (nonatomic, weak, readonly) id layoutableObject; +/** + * The type of ASLayoutable that created this layout + */ +@property (nonatomic, readonly) ASLayoutableType type; + /** * Size of the current layout */ diff --git a/AsyncDisplayKit/Layout/ASLayout.mm b/AsyncDisplayKit/Layout/ASLayout.mm index 56d270b164..0b4db6b58b 100644 --- a/AsyncDisplayKit/Layout/ASLayout.mm +++ b/AsyncDisplayKit/Layout/ASLayout.mm @@ -12,7 +12,6 @@ #import "ASAssert.h" #import "ASDimension.h" -#import "ASDisplayNode.h" #import "ASInternalHelpers.h" #import "ASLayoutSpecUtilities.h" @@ -36,7 +35,7 @@ extern BOOL CGPointIsNull(CGPoint point) @implementation ASLayout -@dynamic frame; +@dynamic frame, type; - (instancetype)initWithLayoutableObject:(id)layoutableObject constrainedSizeRange:(ASSizeRange)sizeRange @@ -159,7 +158,7 @@ extern BOOL CGPointIsNull(CGPoint point) Context context = queue.front(); queue.pop(); - if (self != context.layout && [context.layout.layoutableObject isKindOfClass:[ASDisplayNode class]]) { + if (self != context.layout && context.layout.type == ASLayoutableTypeDisplayNode) { ASLayout *layout = [ASLayout layoutWithLayout:context.layout position:context.absolutePosition]; layout.flattened = YES; [flattenedSublayouts addObject:layout]; @@ -178,6 +177,13 @@ extern BOOL CGPointIsNull(CGPoint point) sublayouts:flattenedSublayouts]; } +#pragma mark - Accessors + +- (ASLayoutableType)type +{ + return _layoutableObject.layoutableType; +} + - (CGRect)frame { CGRect subnodeFrame = CGRectZero; diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 5c84591e78..16cfba3853 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -34,7 +34,8 @@ @implementation ASLayoutSpec // these dynamic properties all defined in ASLayoutOptionsPrivate.m -@dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis, alignSelf, ascender, descender, sizeRange, layoutPosition; +@dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis, + alignSelf, ascender, descender, sizeRange, layoutPosition, layoutableType; @synthesize isFinalLayoutable = _isFinalLayoutable; - (instancetype)init @@ -48,6 +49,11 @@ return self; } +- (ASLayoutableType)layoutableType +{ + return ASLayoutableTypeLayoutSpec; +} + #pragma mark - Layout - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize diff --git a/AsyncDisplayKit/Layout/ASLayoutable.h b/AsyncDisplayKit/Layout/ASLayoutable.h index 325a81535c..6d37e2a4b5 100644 --- a/AsyncDisplayKit/Layout/ASLayoutable.h +++ b/AsyncDisplayKit/Layout/ASLayoutable.h @@ -21,6 +21,11 @@ @class ASLayout; @class ASLayoutSpec; +typedef NS_ENUM(NSUInteger, ASLayoutableType) { + ASLayoutableTypeLayoutSpec, + ASLayoutableTypeDisplayNode +}; + NS_ASSUME_NONNULL_BEGIN /** @@ -41,6 +46,8 @@ NS_ASSUME_NONNULL_BEGIN */ @protocol ASLayoutable +@property (nonatomic, readonly) ASLayoutableType layoutableType; + /** * @abstract Calculate a layout based on given size range. * @@ -50,7 +57,6 @@ NS_ASSUME_NONNULL_BEGIN */ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize; - #pragma mark - Layout options from the Layoutable Protocols #pragma mark - ASStackLayoutable