From 6f37bb40d9a9e3a5a0b3586b293833c09ec2c6f7 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 10 Feb 2016 14:39:46 -0800 Subject: [PATCH] Allow layouts to be accessible in context --- AsyncDisplayKit/ASContextTransitioning.h | 15 ++-- AsyncDisplayKit/ASDisplayNode.mm | 81 ++++++++++++------- .../Private/ASDisplayNodeInternal.h | 4 + AsyncDisplayKit/_ASTransitionContext.h | 10 +-- AsyncDisplayKit/_ASTransitionContext.m | 24 ++++-- 5 files changed, 87 insertions(+), 47 deletions(-) diff --git a/AsyncDisplayKit/ASContextTransitioning.h b/AsyncDisplayKit/ASContextTransitioning.h index 19ea88861c..694ab6bf83 100644 --- a/AsyncDisplayKit/ASContextTransitioning.h +++ b/AsyncDisplayKit/ASContextTransitioning.h @@ -8,6 +8,9 @@ #import +extern NSString * const ASTransitionContextFromLayoutKey; +extern NSString * const ASTransitionContextToLayoutKey; + @protocol ASContextTransitioning /** @@ -16,19 +19,19 @@ - (BOOL)isAnimated; /** - * @abstract The destination layout being transitioned to + * @abstract Retrieve either the "from" or "to" layout */ -- (ASLayout *)layout; +- (ASLayout *)layoutForKey:(NSString *)key; /** - * @abstrat The destination constrainedSize being transitioned to + * @abstract Retrieve either the "from" or "to" constrainedSize */ -- (ASSizeRange)constrainedSize; +- (ASSizeRange)constrainedSizeForKey:(NSString *)key; /** - * @abstract Subnodes in the new layout + * @abstract Retrieve the subnodes from either the "from" or "to" layout */ -- (NSArray *)subnodes; +- (NSArray *)subnodesForKey:(NSString *)key; /** * @abstract Subnodes that have been inserted in the layout transition diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index e5eaa46c33..ca5879bea9 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -610,34 +610,43 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { - return [self measureWithSizeRange:constrainedSize completion:^(ASLayout *pendingLayout, ASSizeRange constrainedSize) { + return [self measureWithSizeRange:constrainedSize completion:^{ if ([[self class] usesImplicitHierarchyManagement]) { [self __implicitlyInsertSubnodes]; [self __implicitlyRemoveSubnodes]; } - [self __applyLayout:pendingLayout constrainedSize:constrainedSize]; + [self __completeLayoutCalculation]; }]; } -- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize completion:(void(^)(ASLayout *, ASSizeRange))completion +- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize completion:(void(^)())completion { ASDisplayNodeAssertThreadAffinity(self); ASDN::MutexLocker l(_propertyLock); if (![self __shouldSize]) return nil; - ASLayout *pendingLayout = _layout; - // only calculate the size if // - we haven't already // - the constrained size range is different if (!_flags.isMeasured || !ASSizeRangeEqualToSizeRange(constrainedSize, _constrainedSize)) { - pendingLayout = [self calculateLayoutThatFits:constrainedSize]; - [self __calculateSubnodeOperationsWithPendingLayout:pendingLayout currentLayout:_layout]; - completion(pendingLayout, constrainedSize); + _previousLayout = _layout; + _layout = [self calculateLayoutThatFits:constrainedSize]; + + ASDisplayNodeAssertTrue(_layout.layoutableObject == self); + ASDisplayNodeAssertTrue(_layout.size.width >= 0.0); + ASDisplayNodeAssertTrue(_layout.size.height >= 0.0); + + _previousConstrainedSize = _constrainedSize; + _constrainedSize = constrainedSize; + + [self __calculateSubnodeOperationsWithLayout:_layout previousLayout:_previousLayout]; + _flags.isMeasured = YES; + + completion(); } - return pendingLayout; + return _layout; } - (ASLayout *)transitionLayoutWithAnimation:(BOOL)animated @@ -648,50 +657,41 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) - (ASLayout *)transitionLayoutWithSizeRange:(ASSizeRange)constrainedSize animated:(BOOL)animated { - return [self measureWithSizeRange:constrainedSize completion:^(ASLayout *pendingLayout, ASSizeRange constrainedSize) { - _transitionContext = [[_ASTransitionContext alloc] initWithLayout:pendingLayout - constrainedSize:constrainedSize - animated:animated - delegate:self]; + return [self measureWithSizeRange:constrainedSize completion:^{ + _transitionContext = [[_ASTransitionContext alloc] initWithAnimation:animated delegate:self]; [self __implicitlyInsertSubnodes]; [self animateLayoutTransition:_transitionContext]; }]; } -- (void)__calculateSubnodeOperationsWithPendingLayout:(ASLayout *)pendingLayout currentLayout:(ASLayout *)currentLayout +- (void)__calculateSubnodeOperationsWithLayout:(ASLayout *)layout previousLayout:(ASLayout *)previousLayout { - if (_layout) { + if (previousLayout) { NSIndexSet *insertions, *deletions; - [currentLayout.immediateSublayouts asdk_diffWithArray:pendingLayout.immediateSublayouts + [previousLayout.immediateSublayouts asdk_diffWithArray:layout.immediateSublayouts insertions:&insertions deletions:&deletions compareBlock:^BOOL(ASLayout *lhs, ASLayout *rhs) { return ASObjectIsEqual(lhs.layoutableObject, rhs.layoutableObject); }]; - filterNodesInLayoutAtIndexes(pendingLayout, insertions, &_insertedSubnodes, &_insertedSubnodePositions); - filterNodesInLayoutAtIndexesWithIntersectingNodes(currentLayout, + filterNodesInLayoutAtIndexes(layout, insertions, &_insertedSubnodes, &_insertedSubnodePositions); + filterNodesInLayoutAtIndexesWithIntersectingNodes(previousLayout, deletions, _insertedSubnodes, &_removedSubnodes, &_removedSubnodePositions); } else { - NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [pendingLayout.immediateSublayouts count])]; - filterNodesInLayoutAtIndexes(pendingLayout, indexes, &_insertedSubnodes, &_insertedSubnodePositions); + NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [layout.immediateSublayouts count])]; + filterNodesInLayoutAtIndexes(layout, indexes, &_insertedSubnodes, &_insertedSubnodePositions); _removedSubnodes = nil; } } -- (void)__applyLayout:(ASLayout *)layout constrainedSize:(ASSizeRange)constrainedSize +- (void)__completeLayoutCalculation { - ASDisplayNodeAssertTrue(layout.layoutableObject == self); - ASDisplayNodeAssertTrue(layout.size.width >= 0.0); - ASDisplayNodeAssertTrue(layout.size.height >= 0.0); - - _layout = layout; - _constrainedSize = constrainedSize; - _flags.isMeasured = YES; _insertedSubnodes = nil; _removedSubnodes = nil; + _previousLayout = nil; [self calculatedLayoutDidChange]; // we generate placeholders at measureWithSizeRange: time so that a node is guaranteed @@ -777,7 +777,7 @@ static inline void filterNodesInLayoutAtIndexesWithIntersectingNodes( - (void)didCompleteTransitionLayout:(id)context { [self __implicitlyRemoveSubnodes]; - [self __applyLayout:context.layout constrainedSize:context.constrainedSize]; + [self __completeLayoutCalculation]; } #pragma mark - Implicit node hierarchy managagment @@ -814,6 +814,27 @@ static inline void filterNodesInLayoutAtIndexesWithIntersectingNodes( return _removedSubnodes; } +- (ASLayout *)transitionContext:(_ASTransitionContext *)context layoutForKey:(NSString *)key +{ + if ([key isEqualToString:ASTransitionContextFromLayoutKey]) { + return _previousLayout; + } else if ([key isEqualToString:ASTransitionContextToLayoutKey]) { + return _layout; + } else { + return nil; + } +} +- (ASSizeRange)transitionContext:(_ASTransitionContext *)context constrainedSizeForKey:(NSString *)key +{ + if ([key isEqualToString:ASTransitionContextFromLayoutKey]) { + return _previousConstrainedSize; + } else if ([key isEqualToString:ASTransitionContextToLayoutKey]) { + return _constrainedSize; + } else { + return ASSizeRangeMake(CGSizeZero, CGSizeZero); + } +} + - (void)transitionContext:(_ASTransitionContext *)context didComplete:(BOOL)didComplete { [self didCompleteTransitionLayout:context]; diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index 378ec535b0..f243fdee79 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -62,8 +62,12 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo // This is the desired contentsScale, not the scale at which the layer's contents should be displayed CGFloat _contentsScaleForDisplay; + ASLayout *_previousLayout; ASLayout *_layout; + + ASSizeRange _previousConstrainedSize; ASSizeRange _constrainedSize; + UIEdgeInsets _hitTestSlop; NSMutableArray *_subnodes; diff --git a/AsyncDisplayKit/_ASTransitionContext.h b/AsyncDisplayKit/_ASTransitionContext.h index cf3ee5b10c..9411d76ac1 100644 --- a/AsyncDisplayKit/_ASTransitionContext.h +++ b/AsyncDisplayKit/_ASTransitionContext.h @@ -16,9 +16,13 @@ @protocol _ASTransitionContextDelegate - (NSArray *)currentSubnodesWithTransitionContext:(_ASTransitionContext *)context; + - (NSArray *)insertedSubnodesWithTransitionContext:(_ASTransitionContext *)context; - (NSArray *)removedSubnodesWithTransitionContext:(_ASTransitionContext *)context; +- (ASLayout *)transitionContext:(_ASTransitionContext *)context layoutForKey:(NSString *)key; +- (ASSizeRange)transitionContext:(_ASTransitionContext *)context constrainedSizeForKey:(NSString *)key; + - (void)transitionContext:(_ASTransitionContext *)context didComplete:(BOOL)didComplete; @end @@ -27,10 +31,6 @@ @property (assign, readonly, nonatomic, getter=isAnimated) BOOL animated; -@property (strong, readonly) ASLayout *layout; - -@property (assign, readonly) ASSizeRange constrainedSize; - -- (instancetype)initWithLayout:(ASLayout *)layout constrainedSize:(ASSizeRange)constrainedSize animated:(BOOL)animated delegate:(id<_ASTransitionContextDelegate>)delegate; +- (instancetype)initWithAnimation:(BOOL)animated delegate:(id<_ASTransitionContextDelegate>)delegate; @end diff --git a/AsyncDisplayKit/_ASTransitionContext.m b/AsyncDisplayKit/_ASTransitionContext.m index 753cac7b36..8c69f194fa 100644 --- a/AsyncDisplayKit/_ASTransitionContext.m +++ b/AsyncDisplayKit/_ASTransitionContext.m @@ -10,6 +10,10 @@ #import "ASLayout.h" + +NSString * const ASTransitionContextFromLayoutKey = @"org.asyncdisplaykit.ASTransitionContextFromLayoutKey"; +NSString * const ASTransitionContextToLayoutKey = @"org.asyncdisplaykit.ASTransitionContextToLayoutKey"; + @interface _ASTransitionContext () @property (weak, nonatomic) id<_ASTransitionContextDelegate> delegate; @@ -18,12 +22,10 @@ @implementation _ASTransitionContext -- (instancetype)initWithLayout:(ASLayout *)layout constrainedSize:(ASSizeRange)constrainedSize animated:(BOOL)animated delegate:(id<_ASTransitionContextDelegate>)delegate +- (instancetype)initWithAnimation:(BOOL)animated delegate:(id<_ASTransitionContextDelegate>)delegate { self = [super init]; if (self) { - _layout = layout; - _constrainedSize = constrainedSize; _animated = animated; _delegate = delegate; } @@ -32,6 +34,16 @@ #pragma mark - ASContextTransitioning Protocol Implementation +- (ASLayout *)layoutForKey:(NSString *)key +{ + return [_delegate transitionContext:self layoutForKey:key]; +} + +- (ASSizeRange)constrainedSizeForKey:(NSString *)key +{ + return [_delegate transitionContext:self constrainedSizeForKey:key]; +} + - (CGRect)initialFrameForNode:(ASDisplayNode *)node { for (ASDisplayNode *subnode in [_delegate currentSubnodesWithTransitionContext:self]) { @@ -44,7 +56,7 @@ - (CGRect)finalFrameForNode:(ASDisplayNode *)node { - for (ASLayout *layout in _layout.sublayouts) { + for (ASLayout *layout in [self layoutForKey:ASTransitionContextToLayoutKey].sublayouts) { if (layout.layoutableObject == node) { return [layout frame]; } @@ -52,10 +64,10 @@ return CGRectZero; } -- (NSArray *)subnodes +- (NSArray *)subnodesForKey:(NSString *)key { NSMutableArray *subnodes = [NSMutableArray array]; - for (ASLayout *sublayout in _layout.immediateSublayouts) { + for (ASLayout *sublayout in [self layoutForKey:key].immediateSublayouts) { [subnodes addObject:(ASDisplayNode *)sublayout.layoutableObject]; } return subnodes;