From d8d76635ff3b525a80e3e84a44b09d635a5dcf34 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 9 Feb 2016 11:05:06 -0800 Subject: [PATCH] Perform animation transition on `measureWithSizeRange` instead of separate method --- AsyncDisplayKit/ASContextTransitioning.h | 4 ++ AsyncDisplayKit/ASDisplayNode+Beta.h | 30 ++++++++++ AsyncDisplayKit/ASDisplayNode+Subclasses.h | 14 ----- AsyncDisplayKit/ASDisplayNode.h | 12 ---- AsyncDisplayKit/ASDisplayNode.mm | 66 +++++++++++++--------- AsyncDisplayKit/_ASTransitionContext.h | 6 +- AsyncDisplayKit/_ASTransitionContext.m | 4 +- 7 files changed, 80 insertions(+), 56 deletions(-) diff --git a/AsyncDisplayKit/ASContextTransitioning.h b/AsyncDisplayKit/ASContextTransitioning.h index 37f2ae6b98..8f0493a3b2 100644 --- a/AsyncDisplayKit/ASContextTransitioning.h +++ b/AsyncDisplayKit/ASContextTransitioning.h @@ -15,6 +15,10 @@ */ - (BOOL)isAnimated; +- (ASLayout *)layout; + +- (ASSizeRange)constrainedSize; + /** @abstract The frame for the given node before the transition began. @discussion Returns CGRectNull if the node was not in the hierarchy before the transition. diff --git a/AsyncDisplayKit/ASDisplayNode+Beta.h b/AsyncDisplayKit/ASDisplayNode+Beta.h index 7cd124a7ae..fff3fa3463 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 "ASContextTransitioning.h" + @interface ASDisplayNode (Beta) + (BOOL)usesImplicitHierarchyManagement; @@ -37,4 +39,32 @@ */ @property (nonatomic, strong) ASDisplayNodeContextModifier didDisplayNodeContentWithRenderingContext; +/** @name Layout Transitioning */ + +/** + @discussion A place to perform your animation. New nodes have been inserted here. You can also use this time to re-order the hierarchy. + */ +- (void)animateLayoutTransition:(id)context; + +/** + @discussion A place to clean up your nodes after the transition + */ +- (void)didCompleteLayoutTransition:(id)context; + +/** + @abstract Invalidates the current layout and begins a relayout of the node to the new layout returned in `calculateLayoutThatFits:`. + + @discussion Animation is optional, but will still proceed through the `transitionLayout` methods with `isAnimated == NO`. + */ +//- (void)transitionLayoutWithAnimation:(BOOL)animated; + +/** + @abstract Invalidates the current layout and begins a relayout of the node to the new layout returned in `calculateLayoutThatFits:`. + + @discussion Animation is optional, but will still proceed through the `transitionLayout` methods with `isAnimated == NO`. + */ +//- (void)transitionLayoutThatFits:(ASSizeRange)constrainedSize animated:(BOOL)animated; + +- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize animated:(BOOL)animated; + @end diff --git a/AsyncDisplayKit/ASDisplayNode+Subclasses.h b/AsyncDisplayKit/ASDisplayNode+Subclasses.h index e571503b81..699b6213ed 100644 --- a/AsyncDisplayKit/ASDisplayNode+Subclasses.h +++ b/AsyncDisplayKit/ASDisplayNode+Subclasses.h @@ -12,7 +12,6 @@ #import #import #import -#import @class ASLayoutSpec; @@ -156,19 +155,6 @@ NS_ASSUME_NONNULL_BEGIN - (void)invalidateCalculatedLayout; -/** @name Layout Transitioning */ - -/** - @discussion A place to perform your animation. New nodes have been inserted here. You can also use this time to re-order the hierarchy. - */ -- (void)animateLayoutTransition:(id)context; - -/** - @discussion A place to clean up your nodes after the transition - */ -- (void)didCompleteLayoutTransition:(id)context; - - /** @name Drawing */ diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index 773ac7d7bf..2f67dc170c 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -571,18 +571,6 @@ NS_ASSUME_NONNULL_BEGIN @end -@interface ASDisplayNode (Transitioning) - -/** - @abstract Invalidates the current layout and begins a relayout of the node to the new layout returned in `calculateLayoutThatFits:`. - - @discussion Animation is optional, but will still proceed through the `transitionLayout` methods with `isAnimated == NO`. - */ -- (void)transitionLayoutWithAnimation:(BOOL)animated; - -@end - - /** * Convenience methods for debugging. */ diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index e84db32824..d87fc4656b 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -644,6 +644,11 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) } - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize +{ + return [self measureWithSizeRange:constrainedSize animated:NO]; +} + +- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize animated:(BOOL)animated { ASDisplayNodeAssertThreadAffinity(self); ASDN::MutexLocker l(_propertyLock); @@ -651,11 +656,13 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) if (![self __shouldSize]) return nil; + ASLayout *newLayout; + // only calculate the size if // - we haven't already // - the constrained size range is different if (!_flags.isMeasured || !ASSizeRangeEqualToSizeRange(constrainedSize, _constrainedSize)) { - ASLayout *newLayout = [self calculateLayoutThatFits:constrainedSize]; + newLayout = [self calculateLayoutThatFits:constrainedSize]; if ([[self class] usesImplicitHierarchyManagement]) { if (_layout) { @@ -674,22 +681,37 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) _deletedSubnodes = nil; } - if (!_deferImmediateHierarchyManagement) { + if (animated) { + [self __transitionToLayout:newLayout constrainedSize:constrainedSize animated:animated]; + } else { [self __implicitlyInsertSubnodes]; [self __implicitlyRemoveSubnodes]; + [self __updateLayout:newLayout constrainedSize:constrainedSize]; } + } else { + // 1.9.x code path + [self __updateLayout:newLayout constrainedSize:constrainedSize]; } - - _layout = newLayout; - _constrainedSize = constrainedSize; - _flags.isMeasured = YES; - [self calculatedLayoutDidChange]; } + return newLayout; +} - ASDisplayNodeAssertTrue(_layout.layoutableObject == self); - ASDisplayNodeAssertTrue(_layout.size.width >= 0.0); - ASDisplayNodeAssertTrue(_layout.size.height >= 0.0); +- (void)__updateLayout:(ASLayout *)layout constrainedSize:(ASSizeRange)constrainedSize +{ + ASDisplayNodeAssertTrue(layout.layoutableObject == self); + ASDisplayNodeAssertTrue(layout.size.width >= 0.0); + ASDisplayNodeAssertTrue(layout.size.height >= 0.0); + _layout = layout; + _constrainedSize = constrainedSize; + _flags.isMeasured = YES; + [self calculatedLayoutDidChange]; + + [self __primePlaceholder]; +} + +- (void)__primePlaceholder +{ // we generate placeholders at measureWithSizeRange: time so that a node is guaranteed // to have a placeholder ready to go. Also, if a node has no size it should not have a placeholder if (self.placeholderEnabled && [self _displaysAsynchronously] && @@ -702,8 +724,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) [self _setupPlaceholderLayerContents]; } } - - return _layout; } /** @@ -1033,23 +1053,12 @@ static inline CATransform3D _calculateTransformFromReferenceToTarget(ASDisplayNo #pragma mark - Layout Transition -- (void)transitionLayoutWithAnimation:(BOOL)animated +- (void)__transitionToLayout:(ASLayout *)layout constrainedSize:(ASSizeRange)constrainedSize animated:(BOOL)animated { - [self transitionLayoutThatFits:_constrainedSize animated:animated]; -} - -- (void)transitionLayoutThatFits:(ASSizeRange)constrainedSize animated:(BOOL)animated -{ - [self invalidateCalculatedLayout]; - _deferImmediateHierarchyManagement = YES; - [self measureWithSizeRange:constrainedSize]; // Generate a new layout - _deferImmediateHierarchyManagement = NO; - [self __transitionLayoutWithAnimation:animated]; -} - -- (void)__transitionLayoutWithAnimation:(BOOL)animated -{ - _transitionContext = [[_ASTransitionContext alloc] initWithAnimation:animated delegate:self]; + _transitionContext = [[_ASTransitionContext alloc] initWithLayout:layout + constrainedSize:constrainedSize + animated:animated + delegate:self]; [self __implicitlyInsertSubnodes]; [self animateLayoutTransition:_transitionContext]; } @@ -1063,6 +1072,7 @@ static inline CATransform3D _calculateTransformFromReferenceToTarget(ASDisplayNo - (void)didCompleteTransitionLayout:(id)context { [self __implicitlyRemoveSubnodes]; + [self __updateLayout:context.layout constrainedSize:context.constrainedSize]; } #pragma mark - Implicit node hierarchy managagment diff --git a/AsyncDisplayKit/_ASTransitionContext.h b/AsyncDisplayKit/_ASTransitionContext.h index ab396514cb..21d6babea7 100644 --- a/AsyncDisplayKit/_ASTransitionContext.h +++ b/AsyncDisplayKit/_ASTransitionContext.h @@ -25,6 +25,10 @@ @property (assign, readonly, nonatomic, getter=isAnimated) BOOL animated; -- (instancetype)initWithAnimation:(BOOL)animated delegate:(id<_ASTransitionContextDelegate>)delegate; +@property (strong, readonly) ASLayout *layout; + +@property (assign, readonly) ASSizeRange constrainedSize; + +- (instancetype)initWithLayout:(ASLayout *)layout constrainedSize:(ASSizeRange)constrainedSize animated:(BOOL)animated delegate:(id<_ASTransitionContextDelegate>)delegate; @end diff --git a/AsyncDisplayKit/_ASTransitionContext.m b/AsyncDisplayKit/_ASTransitionContext.m index 059138e6ae..d5dd171ba4 100644 --- a/AsyncDisplayKit/_ASTransitionContext.m +++ b/AsyncDisplayKit/_ASTransitionContext.m @@ -16,10 +16,12 @@ @implementation _ASTransitionContext -- (instancetype)initWithAnimation:(BOOL)animated delegate:(id<_ASTransitionContextDelegate>)delegate +- (instancetype)initWithLayout:(ASLayout *)layout constrainedSize:(ASSizeRange)constrainedSize animated:(BOOL)animated delegate:(id<_ASTransitionContextDelegate>)delegate { self = [super init]; if (self) { + _layout = layout; + _constrainedSize = constrainedSize; _animated = animated; _delegate = delegate; }