From 8737e242f8cabd625c49c9a3872e5e46e41e5bdd Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 9 Feb 2016 16:11:42 -0800 Subject: [PATCH] Extract measurement and transition into different methods based on feedback --- AsyncDisplayKit/ASDisplayNode+Beta.h | 14 +- AsyncDisplayKit/ASDisplayNode.mm | 199 +++++++++--------- .../Private/ASDisplayNodeInternal.h | 4 + 3 files changed, 115 insertions(+), 102 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode+Beta.h b/AsyncDisplayKit/ASDisplayNode+Beta.h index abc95d3bd7..b4c2042a24 100644 --- a/AsyncDisplayKit/ASDisplayNode+Beta.h +++ b/AsyncDisplayKit/ASDisplayNode+Beta.h @@ -34,28 +34,26 @@ /** * @abstract allow modification of a context after the node's content is drawn - * - * @discussion */ @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. + * @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 + * @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`. + * @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`. */ -- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize animated:(BOOL)animated; +- (ASLayout *)transitionLayoutWithSizeRange:(ASSizeRange)constrainedSize animated:(BOOL)animated; @end diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index fa8855110c..163ca6adeb 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -636,7 +636,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) return _flags.layerBacked; } -#pragma mark - +#pragma mark - Layout measurement calculation - (CGSize)measure:(CGSize)constrainedSize { @@ -645,61 +645,80 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { - return [self measureWithSizeRange:constrainedSize animated:NO]; + return [self measureWithSizeRange:constrainedSize completion:^{ + if ([[self class] usesImplicitHierarchyManagement]) { + [self __implicitlyInsertSubnodes]; + [self __implicitlyRemoveSubnodes]; + } + [self __applyPendingLayout]; + }]; } -- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize animated:(BOOL)animated +- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize completion:(void(^)())completion { ASDisplayNodeAssertThreadAffinity(self); ASDN::MutexLocker l(_propertyLock); - ASLayout *newLayout; - if (![self __shouldSize]) - return newLayout; + return nil; // only calculate the size if // - we haven't already // - the constrained size range is different if (!_flags.isMeasured || !ASSizeRangeEqualToSizeRange(constrainedSize, _constrainedSize)) { - newLayout = [self calculateLayoutThatFits:constrainedSize]; + _pendingLayout = [self calculateLayoutThatFits:constrainedSize]; + _pendingConstrainedSize = constrainedSize; + [self __calculateSubnodeOperations]; - if (_layout) { - NSIndexSet *insertions, *deletions; - [_layout.immediateSublayouts asdk_diffWithArray:newLayout.immediateSublayouts - insertions:&insertions - deletions:&deletions - compareBlock:^BOOL(ASLayout *lhs, ASLayout *rhs) { - return ASObjectIsEqual(lhs.layoutableObject, rhs.layoutableObject); - }]; - _insertedSubnodes = [self _nodesInLayout:newLayout atIndexes:insertions]; - _deletedSubnodes = [self _nodesInLayout:_layout atIndexes:deletions filterNodes:_insertedSubnodes]; - } else { - NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [newLayout.immediateSublayouts count])]; - _insertedSubnodes = [self _nodesInLayout:newLayout atIndexes:indexes]; - _deletedSubnodes = nil; - } - - if (animated) { - [self __transitionToLayout:newLayout constrainedSize:constrainedSize animated:animated]; - } else { - if ([[self class] usesImplicitHierarchyManagement]) { - [self __implicitlyInsertSubnodes]; - [self __implicitlyRemoveSubnodes]; - } - [self __updateLayout:newLayout constrainedSize:constrainedSize]; - } + completion(); } - return newLayout; + + return _pendingLayout; } -- (void)__updateLayout:(ASLayout *)layout constrainedSize:(ASSizeRange)constrainedSize +- (ASLayout *)transitionLayoutWithSizeRange:(ASSizeRange)constrainedSize animated:(BOOL)animated { - ASDisplayNodeAssertTrue(layout.layoutableObject == self); - ASDisplayNodeAssertTrue(layout.size.width >= 0.0); - ASDisplayNodeAssertTrue(layout.size.height >= 0.0); + [self invalidateCalculatedLayout]; + return [self measureWithSizeRange:constrainedSize completion:^{ + _transitionContext = [[_ASTransitionContext alloc] initWithLayout:_pendingLayout + constrainedSize:constrainedSize + animated:animated + delegate:self]; + [self __implicitlyInsertSubnodes]; + [self animateLayoutTransition:_transitionContext]; + }]; +} + +- (void)__calculateSubnodeOperations +{ + if (_layout) { + NSIndexSet *insertions, *deletions; + [_layout.immediateSublayouts asdk_diffWithArray:_pendingLayout.immediateSublayouts + insertions:&insertions + deletions:&deletions + compareBlock:^BOOL(ASLayout *lhs, ASLayout *rhs) { + return ASObjectIsEqual(lhs.layoutableObject, rhs.layoutableObject); + }]; + _insertedSubnodes = [self _nodesInLayout:_pendingLayout atIndexes:insertions]; + _deletedSubnodes = [self _nodesInLayout:_layout atIndexes:deletions filterNodes:_insertedSubnodes]; + } else { + NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [_pendingLayout.immediateSublayouts count])]; + _insertedSubnodes = [self _nodesInLayout:_pendingLayout atIndexes:indexes]; + _deletedSubnodes = nil; + } +} + +- (void)__applyPendingLayout +{ + ASDisplayNodeAssertTrue(_pendingLayout.layoutableObject == self); + ASDisplayNodeAssertTrue(_pendingLayout.size.width >= 0.0); + ASDisplayNodeAssertTrue(_pendingLayout.size.height >= 0.0); + + _layout = _pendingLayout; + _pendingLayout = nil; + + _constrainedSize = _pendingConstrainedSize; + _pendingConstrainedSize = ASSizeRangeMake(CGSizeZero, CGSizeZero); - _layout = layout; - _constrainedSize = constrainedSize; _flags.isMeasured = YES; [self calculatedLayoutDidChange]; @@ -760,6 +779,53 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) // subclass override } +#pragma mark - Layout Transition + +- (void)animateLayoutTransition:(id)context +{ + [self __layoutSublayouts]; + [context completeTransition:YES]; +} + +- (void)didCompleteTransitionLayout:(id)context +{ + [self __implicitlyRemoveSubnodes]; + [self __applyPendingLayout]; +} + +#pragma mark - Implicit node hierarchy managagment + +- (void)__implicitlyInsertSubnodes +{ + for (_ASDisplayNodePosition *position in _insertedSubnodes) { + [self insertSubnode:position.node atIndex:position.index]; + } + _insertedSubnodes = nil; +} + +- (void)__implicitlyRemoveSubnodes +{ + for (_ASDisplayNodePosition *position in _deletedSubnodes) { + [position.node removeFromSupernode]; + } + _deletedSubnodes = nil; +} + +#pragma mark - _ASTransitionContextDelegate + +- (NSArray *)currentSubnodesWithTransitionContext:(_ASTransitionContext *)context +{ + return _subnodes; +} + +- (void)transitionContext:(_ASTransitionContext *)context didComplete:(BOOL)didComplete +{ + [self didCompleteTransitionLayout:context]; + _transitionContext = nil; +} + +#pragma mark - Asynchronous display + - (BOOL)displaysAsynchronously { ASDN::MutexLocker l(_propertyLock); @@ -1047,61 +1113,6 @@ static inline CATransform3D _calculateTransformFromReferenceToTarget(ASDisplayNo return CGRectApplyAffineTransform(rect, flattenedTransform); } -#pragma mark - Layout Transition - -- (void)__transitionToLayout:(ASLayout *)layout constrainedSize:(ASSizeRange)constrainedSize animated:(BOOL)animated -{ - _transitionContext = [[_ASTransitionContext alloc] initWithLayout:layout - constrainedSize:constrainedSize - animated:animated - delegate:self]; - [self __implicitlyInsertSubnodes]; - [self animateLayoutTransition:_transitionContext]; -} - -- (void)animateLayoutTransition:(id)context -{ - [self __layoutSublayouts]; - [context completeTransition:YES]; -} - -- (void)didCompleteTransitionLayout:(id)context -{ - [self __implicitlyRemoveSubnodes]; - [self __updateLayout:context.layout constrainedSize:context.constrainedSize]; -} - -#pragma mark - Implicit node hierarchy managagment - -- (void)__implicitlyInsertSubnodes -{ - for (_ASDisplayNodePosition *position in _insertedSubnodes) { - [self insertSubnode:position.node atIndex:position.index]; - } - _insertedSubnodes = nil; -} - -- (void)__implicitlyRemoveSubnodes -{ - for (_ASDisplayNodePosition *position in _deletedSubnodes) { - [position.node removeFromSupernode]; - } - _deletedSubnodes = nil; -} - -#pragma mark - _ASTransitionContextDelegate - -- (NSArray *)currentSubnodesWithTransitionContext:(_ASTransitionContext *)context -{ - return _subnodes; -} - -- (void)transitionContext:(_ASTransitionContext *)context didComplete:(BOOL)didComplete -{ - [self didCompleteTransitionLayout:context]; - _transitionContext = nil; -} - #pragma mark - _ASDisplayLayerDelegate - (void)willDisplayAsyncLayer:(_ASDisplayLayer *)layer diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index 23575f1398..d1dc25b2c5 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -61,7 +61,11 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo CGFloat _contentsScaleForDisplay; ASLayout *_layout; + ASLayout *_pendingLayout; + ASSizeRange _constrainedSize; + ASSizeRange _pendingConstrainedSize; + UIEdgeInsets _hitTestSlop; NSMutableArray *_subnodes;