From 6d5bd6e969ba8c426bf0abe3d83597d739148afd Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Wed, 26 Oct 2016 11:26:57 -0700 Subject: [PATCH] Cleanup calculateLayoutThatFits: (#2480) --- AsyncDisplayKit/ASDisplayNode+Beta.h | 5 - AsyncDisplayKit/ASDisplayNode+Subclasses.h | 25 ++- AsyncDisplayKit/ASDisplayNode.mm | 172 ++++++++++-------- AsyncDisplayKit/Details/ASEnvironment.h | 5 +- .../Layout/ASAbsoluteLayoutSpec.mm | 10 - .../Layout/ASLayoutSpec+Subclasses.mm | 4 - AsyncDisplayKit/Layout/ASLayoutSpec.mm | 31 ---- AsyncDisplayKit/Layout/ASStackLayoutSpec.mm | 9 - examples/ASDKgram/Sample/CommentsNode.m | 3 +- examples/ASDKgram/Sample/PhotoCellNode.m | 26 ++- 10 files changed, 123 insertions(+), 167 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode+Beta.h b/AsyncDisplayKit/ASDisplayNode+Beta.h index 40ff9e10e5..e68d8827ee 100644 --- a/AsyncDisplayKit/ASDisplayNode+Beta.h +++ b/AsyncDisplayKit/ASDisplayNode+Beta.h @@ -63,9 +63,6 @@ typedef struct { + (BOOL)suppressesInvalidCollectionUpdateExceptions AS_WARN_UNUSED_RESULT; + (void)setSuppressesInvalidCollectionUpdateExceptions:(BOOL)suppresses; -/** @name Layout */ - - /** * @abstract Recursively ensures node and all subnodes are displayed. * @see Full documentation in ASDisplayNode+FrameworkPrivate.h @@ -97,8 +94,6 @@ typedef struct { */ @property (nonatomic, assign, readonly) ASDisplayNodePerformanceMeasurements performanceMeasurements; -/** @name Layout Transitioning */ - /** * @abstract Currently used by ASNetworkImageNode and ASMultiplexImageNode to allow their placeholders to stay if they are loading an image from the network. * Otherwise, a display pass is scheduled and completes, but does not actually draw anything - and ASDisplayNode considers the element finished. diff --git a/AsyncDisplayKit/ASDisplayNode+Subclasses.h b/AsyncDisplayKit/ASDisplayNode+Subclasses.h index 4b6a7be8cd..d82353e69c 100644 --- a/AsyncDisplayKit/ASDisplayNode+Subclasses.h +++ b/AsyncDisplayKit/ASDisplayNode+Subclasses.h @@ -41,6 +41,7 @@ NS_ASSUME_NONNULL_BEGIN @interface ASDisplayNode (Subclassing) +#pragma mark - Properties /** @name Properties */ /** @@ -64,9 +65,9 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nullable, nonatomic, readonly, assign) ASLayout *calculatedLayout; +#pragma mark - View Lifecycle /** @name View Lifecycle */ - /** * @abstract Called on the main thread immediately after self.view is created. * @@ -75,9 +76,9 @@ NS_ASSUME_NONNULL_BEGIN - (void)didLoad ASDISPLAYNODE_REQUIRES_SUPER; +#pragma mark - Layout /** @name Layout */ - /** * @abstract Called on the main thread by the view's -layoutSubviews. * @@ -101,6 +102,8 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)calculatedLayoutDidChange ASDISPLAYNODE_REQUIRES_SUPER; + +#pragma mark - Layout calculation /** @name Layout calculation */ /** @@ -159,9 +162,9 @@ NS_ASSUME_NONNULL_BEGIN * * @note This method should not be called directly outside of ASDisplayNode; use -measure: or -calculatedLayout instead. * - * @warning Subclasses that implement -layoutSpecThatFits: must not also use .layoutSpecBlock. Doing so will trigger - * an exception. A future version of the framework may support using both, calling them serially, with the - * .layoutSpecBlock superseding any values set by the method override. + * @warning Subclasses that implement -layoutSpecThatFits: must not use .layoutSpecBlock. Doing so will trigger an + * exception. A future version of the framework may support using both, calling them serially, with the .layoutSpecBlock + * superseding any values set by the method override. */ - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize; @@ -174,9 +177,9 @@ NS_ASSUME_NONNULL_BEGIN - (void)invalidateCalculatedLayout; +#pragma mark - Drawing /** @name Drawing */ - /** * @summary Delegate method to draw layer contents into a CGBitmapContext. The current UIGraphics context will be set * to an appropriate context. @@ -407,9 +410,9 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign, readonly) CGFloat contentsScaleForDisplay; +#pragma mark - Touch handling /** @name Touch handling */ - /** * @abstract Tells the node when touches began in its view. * @@ -443,9 +446,9 @@ NS_ASSUME_NONNULL_BEGIN - (void)touchesCancelled:(nullable NSSet *)touches withEvent:(nullable UIEvent *)event ASDISPLAYNODE_REQUIRES_SUPER; +#pragma mark - Managing Gesture Recognizers /** @name Managing Gesture Recognizers */ - /** * @abstract Asks the node if a gesture recognizer should continue tracking touches. * @@ -454,8 +457,9 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer; -/** @name Hit Testing */ +#pragma mark - Hit Testing +/** @name Hit Testing */ /** * @abstract Returns the view that contains the point. @@ -472,6 +476,8 @@ NS_ASSUME_NONNULL_BEGIN */ - (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event; + +#pragma mark - Placeholders /** @name Placeholders */ /** @@ -492,6 +498,7 @@ NS_ASSUME_NONNULL_BEGIN - (nullable UIImage *)placeholderImage; +#pragma mark - Description /** @name Description */ /** diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index edb1967acf..7b18bc5fd5 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -186,10 +186,10 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) // At most a layoutSpecBlock or one of the three layout methods is overridden #define __ASDisplayNodeCheckForLayoutMethodOverrides \ ASDisplayNodeAssert(_layoutSpecBlock != NULL || \ - (ASDisplayNodeSubclassOverridesSelector(self.class, @selector(calculateSizeThatFits:)) ? 1 : 0) \ + ((ASDisplayNodeSubclassOverridesSelector(self.class, @selector(calculateSizeThatFits:)) ? 1 : 0) \ + (ASDisplayNodeSubclassOverridesSelector(self.class, @selector(layoutSpecThatFits:)) ? 1 : 0) \ - + (ASDisplayNodeSubclassOverridesSelector(self.class, @selector(calculateLayoutThatFits:)) ? 1 : 0) <= 1, \ - @"Subclass %@ must at least provide a layoutSpecBlock or override at most one of the three layout methods: calculateLayoutThatFits, layoutSpecThatFits or calculateSizeThatFits", NSStringFromClass(self.class)) + + (ASDisplayNodeSubclassOverridesSelector(self.class, @selector(calculateLayoutThatFits:)) ? 1 : 0)) <= 1, \ + @"Subclass %@ must at least provide a layoutSpecBlock or override at most one of the three layout methods: calculateLayoutThatFits:, layoutSpecThatFits:, or calculateSizeThatFits:", NSStringFromClass(self.class)) + (void)initialize { @@ -199,14 +199,14 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) // Subclasses should never override these. Use unused to prevent warnings __unused NSString *classString = NSStringFromClass(self); - ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedSize)), @"Subclass %@ must not override calculatedSize method", classString); - ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedLayout)), @"Subclass %@ must not override calculatedLayout method", classString); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedSize)), @"Subclass %@ must not override calculatedSize method.", classString); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedLayout)), @"Subclass %@ must not override calculatedLayout method.", classString); ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(measure:)), @"Subclass %@ must not override measure: method", classString); ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(measureWithSizeRange:)), @"Subclass %@ must not override measureWithSizeRange: method. Instead overwrite calculateLayoutThatFits:", classString); - ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:)), @"Subclass %@ must not override layoutThatFits: method", classString); - ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:parentSize:)), @"Subclass %@ must not override layoutThatFits:parentSize method", classString); - ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearContents)), @"Subclass %@ must not override recursivelyClearContents method", classString); - ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearFetchedData)), @"Subclass %@ must not override recursivelyClearFetchedData method", classString); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:)), @"Subclass %@ must not override layoutThatFits: method. Instead overwrite calculateLayoutThatFits:.", classString); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:parentSize:)), @"Subclass %@ must not override layoutThatFits:parentSize method. Instead overwrite calculateLayoutThatFits:.", classString); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearContents)), @"Subclass %@ must not override recursivelyClearContents method.", classString); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearFetchedData)), @"Subclass %@ must not override recursivelyClearFetchedData method.", classString); } // Below we are pre-calculating values per-class and dynamically adding a method (_staticInitialize) to populate these values @@ -232,18 +232,17 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) #if DEBUG - // Check if subnodes where modified during layoutSpecThatFits: - if (self == [ASDisplayNode class] || ASSubclassOverridesSelector([ASDisplayNode class], self, @selector(layoutSpecThatFits:))) - { - __block IMP originalLayoutSpecThatFitsIMP = ASReplaceMethodWithBlock(self, @selector(layoutSpecThatFits:), ^(ASDisplayNode *_self, ASSizeRange sizeRange) { + // Check if subnodes where modified during the creation of the layout + if (self == [ASDisplayNode class]) { + __block IMP originalLayoutSpecThatFitsIMP = ASReplaceMethodWithBlock(self, @selector(_layoutElementThatFits:), ^(ASDisplayNode *_self, ASSizeRange sizeRange) { NSArray *oldSubnodes = _self.subnodes; - ASLayoutSpec *layoutSpec = ((ASLayoutSpec *( *)(id, SEL, ASSizeRange))originalLayoutSpecThatFitsIMP)(_self, @selector(layoutSpecThatFits:), sizeRange); + ASLayoutSpec *layoutElement = ((ASLayoutSpec *( *)(id, SEL, ASSizeRange))originalLayoutSpecThatFitsIMP)(_self, @selector(_layoutElementThatFits:), sizeRange); NSArray *subnodes = _self.subnodes; - ASDisplayNodeAssert(oldSubnodes.count == subnodes.count, @"Adding or removing nodes in layoutSpecThatFits: is verboten."); + ASDisplayNodeAssert(oldSubnodes.count == subnodes.count, @"Adding or removing nodes in layoutSpecBlock or layoutSpecThatFits: is not allowed and can cause unexpected behavior."); for (NSInteger i = 0; i < oldSubnodes.count; i++) { - ASDisplayNodeAssert(oldSubnodes[i] == subnodes[i], @"Adding and removing nodes in layoutSpecThatFits: is verboten."); + ASDisplayNodeAssert(oldSubnodes[i] == subnodes[i], @"Adding or removing nodes in layoutSpecBlock or layoutSpecThatFits: is not allowed and can cause unexpected behavior."); } - return layoutSpec; + return layoutElement; }); } #endif @@ -2425,62 +2424,67 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) __ASDisplayNodeCheckForLayoutMethodOverrides; ASDN::MutexLocker l(__instanceLock__); - if ((_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) || _layoutSpecBlock != NULL) { - BOOL measureLayoutSpec = _measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutSpec; - if (measureLayoutSpec) { - _layoutSpecNumberOfPasses++; - } - ASLayoutSpec *layoutSpec = ({ - ASDN::SumScopeTimer t(_layoutSpecTotalTime, measureLayoutSpec); - [self layoutSpecThatFits:constrainedSize]; - }); - -#if AS_DEDUPE_LAYOUT_SPEC_TREE - NSSet *duplicateElements = [layoutSpec findDuplicatedElementsInSubtree]; - if (duplicateElements.count > 0) { - ASDisplayNodeFailAssert(@"Node %@ returned a layout spec that contains the same elements in multiple positions. Elements: %@", self, duplicateElements); - // Use an empty layout spec to avoid crash. - layoutSpec = [[ASLayoutSpec alloc] init]; - } -#endif - - ASDisplayNodeAssert(layoutSpec.isMutable, @"Node %@ returned layout spec %@ that has already been used. Layout specs should always be regenerated.", self, layoutSpec); - - layoutSpec.parent = self; // This causes upward propogation of any non-default layoutElement values. - - // manually propagate the trait collection here so that any layoutSpec children of layoutSpec will get a traitCollection - { - ASDN::SumScopeTimer t(_layoutSpecTotalTime, measureLayoutSpec); - ASEnvironmentStatePropagateDown(layoutSpec, self.environmentTraitCollection); - } - - layoutSpec.isMutable = NO; - BOOL measureLayoutComputation = _measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutComputation; - if (measureLayoutComputation) { - _layoutComputationNumberOfPasses++; - } - - ASLayout *layout = ({ - ASDN::SumScopeTimer t(_layoutComputationTotalTime, measureLayoutComputation); - [layoutSpec layoutThatFits:constrainedSize]; - }); - - ASDisplayNodeAssertNotNil(layout, @"[ASLayoutSpec measureWithSizeRange:] should never return nil! %@, %@", self, layoutSpec); - - // Make sure layoutElementObject of the root layout is `self`, so that the flattened layout will be structurally correct. - BOOL isFinalLayoutElement = (layout.layoutElement != self); - if (isFinalLayoutElement) { - layout.position = CGPointZero; - layout = [ASLayout layoutWithLayoutElement:self size:layout.size sublayouts:@[layout]]; - } - ASDisplayNodeLogEvent(self, @"computedLayout: %@", layout); - return [layout filteredNodeLayoutTree]; - } else { + // Manual size calculation via calculateSizeThatFits: + if (((_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) || + (_layoutSpecBlock != NULL)) == NO) { CGSize size = [self calculateSizeThatFits:constrainedSize.max]; ASDisplayNodeLogEvent(self, @"calculatedSize: %@", NSStringFromCGSize(size)); return [ASLayout layoutWithLayoutElement:self size:ASSizeRangeClamp(constrainedSize, size) sublayouts:nil]; } + + // Size calcualtion with layout elements + BOOL measureLayoutSpec = _measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutSpec; + if (measureLayoutSpec) { + _layoutSpecNumberOfPasses++; + } + + // Get layout element from the node + id layoutElement = [self _layoutElementThatFits:constrainedSize]; + + // Certain properties are necessary to set on an element of type ASLayoutSpec + if (layoutElement.layoutElementType == ASLayoutElementTypeLayoutSpec) { + ASLayoutSpec *layoutSpec = (ASLayoutSpec *)layoutElement; + + NSSet *duplicateElements = [layoutSpec findDuplicatedElementsInSubtree]; + if (duplicateElements.count > 0) { + ASDisplayNodeFailAssert(@"Node %@ returned a layout spec that contains the same elements in multiple positions. Elements: %@", self, duplicateElements); + // Use an empty layout spec to avoid crashes + layoutSpec = [[ASLayoutSpec alloc] init]; + } + + ASDisplayNodeAssert(layoutSpec.isMutable, @"Node %@ returned layout spec %@ that has already been used. Layout specs should always be regenerated.", self, layoutSpec); + layoutSpec.parent = self; + layoutSpec.isMutable = NO; + } + + // Manually propagate the trait collection here so that any layoutSpec children of layoutSpec will get a traitCollection + { + ASDN::SumScopeTimer t(_layoutSpecTotalTime, measureLayoutSpec); + ASEnvironmentStatePropagateDown(layoutElement, [self environmentTraitCollection]); + } + + BOOL measureLayoutComputation = _measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutComputation; + if (measureLayoutComputation) { + _layoutComputationNumberOfPasses++; + } + + // Layout element layout creation + ASLayout *layout = ({ + ASDN::SumScopeTimer t(_layoutComputationTotalTime, measureLayoutComputation); + [layoutElement layoutThatFits:constrainedSize]; + }); + ASDisplayNodeAssertNotNil(layout, @"[ASLayoutElement layoutThatFits:] should never return nil! %@, %@", self, layout); + + // Make sure layoutElementObject of the root layout is `self`, so that the flattened layout will be structurally correct. + BOOL isFinalLayoutElement = (layout.layoutElement != self); + if (isFinalLayoutElement) { + layout.position = CGPointZero; + layout = [ASLayout layoutWithLayoutElement:self size:layout.size sublayouts:@[layout]]; + } + ASDisplayNodeLogEvent(self, @"computedLayout: %@", layout); + + return [layout filteredNodeLayoutTree]; } - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize @@ -2490,15 +2494,28 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) return CGSizeZero; } +- (id)_layoutElementThatFits:(ASSizeRange)constrainedSize +{ + __ASDisplayNodeCheckForLayoutMethodOverrides; + + BOOL measureLayoutSpec = _measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutSpec; + if (_layoutSpecBlock != NULL) { + return ({ + ASDN::MutexLocker l(__instanceLock__); + ASDN::SumScopeTimer t(_layoutSpecTotalTime, measureLayoutSpec); + _layoutSpecBlock(self, constrainedSize); + }); + } else { + return ({ + ASDN::SumScopeTimer t(_layoutSpecTotalTime, measureLayoutSpec); + [self layoutSpecThatFits:constrainedSize]; + }); + } +} + - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize { __ASDisplayNodeCheckForLayoutMethodOverrides; - - ASDN::MutexLocker l(__instanceLock__); - - if (_layoutSpecBlock != NULL) { - return _layoutSpecBlock(self, constrainedSize); - } ASDisplayNodeAssert(NO, @"-[ASDisplayNode layoutSpecThatFits:] should never return an empty value. One way this is caused is by calling -[super layoutSpecThatFits:] which is not currently supported."); return [[ASLayoutSpec alloc] init]; @@ -2506,7 +2523,7 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) - (void)setLayoutSpecBlock:(ASLayoutSpecBlock)layoutSpecBlock { - // For now there should never be a overwrite of layoutSpecThatFits: and a layoutSpecThatFitsBlock: be provided + // For now there should never be an overwrite of layoutSpecThatFits: / layoutElementThatFits: and a layoutSpecBlock ASDisplayNodeAssert(!(_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits), @"Overwriting layoutSpecThatFits: and providing a layoutSpecBlock block is currently not supported"); ASDN::MutexLocker l(__instanceLock__); @@ -3421,11 +3438,6 @@ static const char *ASDisplayNodeDrawingPriorityKey = "ASDrawingPriority"; return self.subnodes; } -- (BOOL)supportsUpwardPropagation -{ - return ASEnvironmentStatePropagationEnabled(); -} - - (BOOL)supportsTraitsCollectionPropagation { return ASEnvironmentStateTraitCollectionPropagationEnabled(); diff --git a/AsyncDisplayKit/Details/ASEnvironment.h b/AsyncDisplayKit/Details/ASEnvironment.h index 4fcb27f356..e8f2bf6159 100644 --- a/AsyncDisplayKit/Details/ASEnvironment.h +++ b/AsyncDisplayKit/Details/ASEnvironment.h @@ -110,8 +110,9 @@ ASDISPLAYNODE_EXTERN_C_END /// Returns all children of an object which class conforms to the ASEnvironment protocol - (nullable NSArray> *)children; -/// Classes should implement this method and return YES / NO dependent if upward propagation is enabled or not -- (BOOL)supportsUpwardPropagation; +/// Classes should implement this method and return YES / NO dependent if upward propagation is enabled or not +// Currently this is disabled as propagation of any attributions besides trait collections is not supported at the moment +// - (BOOL)supportsUpwardPropagation; /// Classes should implement this method and return YES / NO dependent if downware propagation is enabled or not - (BOOL)supportsTraitsCollectionPropagation; diff --git a/AsyncDisplayKit/Layout/ASAbsoluteLayoutSpec.mm b/AsyncDisplayKit/Layout/ASAbsoluteLayoutSpec.mm index 38be927ef6..c4506ad72d 100644 --- a/AsyncDisplayKit/Layout/ASAbsoluteLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASAbsoluteLayoutSpec.mm @@ -100,16 +100,6 @@ @end -#pragma mark - ASEnvironment - -@implementation ASAbsoluteLayoutSpec (ASEnvironment) - -- (BOOL)supportsUpwardPropagation -{ - return NO; -} - -@end #pragma mark - Debugging diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec+Subclasses.mm b/AsyncDisplayKit/Layout/ASLayoutSpec+Subclasses.mm index 517f25aac9..ed186d4a0c 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec+Subclasses.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec+Subclasses.mm @@ -88,10 +88,6 @@ // Replace object at the given index with the layoutElement _childrenArray[index] = layoutElement; - - // TODO: Should we propagate up the layoutElement at it could happen that multiple children will propagated up their - // layout options and one child will overwrite values from another child - // [self propagateUpLayoutElement:finalLayoutElement]; } - (id)childAtIndex:(NSUInteger)index diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 72fe214755..ef9643e44e 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -105,19 +105,6 @@ return [ASLayout layoutWithLayoutElement:self size:constrainedSize.min]; } - -#pragma mark - Parent - -- (void)setParent:(id)parent -{ - // FIXME: Locking should be evaluated here. _parent is not widely used yet, though. - _parent = parent; - - if ([parent supportsUpwardPropagation]) { - ASEnvironmentStatePropagateUp(parent, self.environmentState.layoutOptionsState); - } -} - #pragma mark - Child - (void)setChild:(id)child @@ -129,7 +116,6 @@ id finalLayoutElement = [self layoutElementToAddFromLayoutElement:child]; if (finalLayoutElement) { _childrenArray[0] = finalLayoutElement; - [self propagateUpLayoutElement:finalLayoutElement]; } } else { if (_childrenArray.count) { @@ -185,28 +171,11 @@ _environmentState = environmentState; } -// Subclasses can override this method to return NO, because upward propagation is not enabled if a layout -// specification has more than one child. Currently ASStackLayoutSpec and ASAbsoluteLayoutSpec are currently -// the specifications that are known to have more than one. -- (BOOL)supportsUpwardPropagation -{ - return ASEnvironmentStatePropagationEnabled(); -} - - (BOOL)supportsTraitsCollectionPropagation { return ASEnvironmentStateTraitCollectionPropagationEnabled(); } -- (void)propagateUpLayoutElement:(id)layoutElement -{ - if ([layoutElement isKindOfClass:[ASLayoutSpec class]]) { - [(ASLayoutSpec *)layoutElement setParent:self]; // This will trigger upward propogation if needed. - } else if ([self supportsUpwardPropagation]) { - ASEnvironmentStatePropagateUp(self, layoutElement.environmentState.layoutOptionsState); // Probably an ASDisplayNode - } -} - - (ASEnvironmentTraitCollection)environmentTraitCollection { return _environmentState.environmentTraitCollection; diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm index 463327dcf6..2f261b681f 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm @@ -182,15 +182,6 @@ @end -@implementation ASStackLayoutSpec (ASEnvironment) - -- (BOOL)supportsUpwardPropagation -{ - return NO; -} - -@end - @implementation ASStackLayoutSpec (Debugging) #pragma mark - ASLayoutElementAsciiArtProtocol diff --git a/examples/ASDKgram/Sample/CommentsNode.m b/examples/ASDKgram/Sample/CommentsNode.m index 56e270faa5..0d3b936c4e 100644 --- a/examples/ASDKgram/Sample/CommentsNode.m +++ b/examples/ASDKgram/Sample/CommentsNode.m @@ -45,8 +45,7 @@ { ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec]; verticalStack.spacing = INTER_COMMENT_SPACING; - [verticalStack setChildren:_commentNodes]; - + verticalStack.children = [_commentNodes copy]; return verticalStack; } diff --git a/examples/ASDKgram/Sample/PhotoCellNode.m b/examples/ASDKgram/Sample/PhotoCellNode.m index db5f8b55b3..2926b3296e 100644 --- a/examples/ASDKgram/Sample/PhotoCellNode.m +++ b/examples/ASDKgram/Sample/PhotoCellNode.m @@ -116,30 +116,26 @@ _photoLocationLabel.style.flexShrink = 1.0; _userNameLabel.style.flexShrink = 1.0; - ASStackLayoutSpec *headerSubStack = [ASStackLayoutSpec verticalStackLayoutSpec]; + ASStackLayoutSpec *headerSubStack = [ASStackLayoutSpec verticalStackLayoutSpec]; headerSubStack.style.flexShrink = 1.0; - if (_photoLocationLabel.attributedText) { - [headerSubStack setChildren:@[_userNameLabel, _photoLocationLabel]]; - } else { - [headerSubStack setChildren:@[_userNameLabel]]; - } - + headerSubStack.children = _photoLocationLabel.attributedText ? @[_userNameLabel, _photoLocationLabel] + : @[_userNameLabel]; // header stack // constrain avatar image frame size _userAvatarImageView.style.preferredSize = CGSizeMake(USER_IMAGE_HEIGHT, USER_IMAGE_HEIGHT); - _photoTimeIntervalSincePostLabel.style.spacingBefore = HORIZONTAL_BUFFER; // to remove double spaces around spacer + _photoTimeIntervalSincePostLabel.style.spacingBefore = HORIZONTAL_BUFFER; // to remove double spaces around spacer - ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init]; // FIXME: long locations overflow post time - set max size? + ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init]; // FIXME: long locations overflow post time - set max size? spacer.style.flexGrow = 1.0; UIEdgeInsets avatarInsets = UIEdgeInsetsMake(HORIZONTAL_BUFFER, 0, HORIZONTAL_BUFFER, HORIZONTAL_BUFFER); ASInsetLayoutSpec *avatarInset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:avatarInsets child:_userAvatarImageView]; ASStackLayoutSpec *headerStack = [ASStackLayoutSpec horizontalStackLayoutSpec]; - headerStack.alignItems = ASStackLayoutAlignItemsCenter; // center items vertically in horizontal stack - headerStack.justifyContent = ASStackLayoutJustifyContentStart; // justify content to the left side of the header stack - [headerStack setChildren:@[avatarInset, headerSubStack, spacer, _photoTimeIntervalSincePostLabel]]; + headerStack.alignItems = ASStackLayoutAlignItemsCenter; // center items vertically in horizontal stack + headerStack.justifyContent = ASStackLayoutJustifyContentStart; // justify content to the left side of the header stack + headerStack.children = @[avatarInset, headerSubStack, spacer, _photoTimeIntervalSincePostLabel]; // header inset stack UIEdgeInsets insets = UIEdgeInsetsMake(0, HORIZONTAL_BUFFER, 0, HORIZONTAL_BUFFER); @@ -148,7 +144,7 @@ // footer stack ASStackLayoutSpec *footerStack = [ASStackLayoutSpec verticalStackLayoutSpec]; footerStack.spacing = VERTICAL_BUFFER; - [footerStack setChildren:@[_photoLikesLabel, _photoDescriptionLabel, _photoCommentsView]]; + footerStack.children = @[_photoLikesLabel, _photoDescriptionLabel, _photoCommentsView]; // footer inset stack UIEdgeInsets footerInsets = UIEdgeInsetsMake(VERTICAL_BUFFER, HORIZONTAL_BUFFER, VERTICAL_BUFFER, HORIZONTAL_BUFFER); @@ -161,8 +157,8 @@ _photoImageView.style.preferredSize = CGSizeMake(cellWidth, cellWidth); ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec]; - verticalStack.alignItems = ASStackLayoutAlignItemsStretch; // stretch headerStack to fill horizontal space - [verticalStack setChildren:@[headerWithInset, _photoImageView, footerWithInset]]; + verticalStack.alignItems = ASStackLayoutAlignItemsStretch; // stretch headerStack to fill horizontal space + verticalStack.children = @[headerWithInset, _photoImageView, footerWithInset]; return verticalStack; }