Allow layouts to be accessible in context

This commit is contained in:
Levi McCallum 2016-02-10 14:39:46 -08:00
parent 6aae68ead4
commit 6f37bb40d9
5 changed files with 87 additions and 47 deletions

View File

@ -8,6 +8,9 @@
#import <AsyncDisplayKit/ASDisplayNode.h> #import <AsyncDisplayKit/ASDisplayNode.h>
extern NSString * const ASTransitionContextFromLayoutKey;
extern NSString * const ASTransitionContextToLayoutKey;
@protocol ASContextTransitioning <NSObject> @protocol ASContextTransitioning <NSObject>
/** /**
@ -16,19 +19,19 @@
- (BOOL)isAnimated; - (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<ASDisplayNode *> *)subnodes; - (NSArray<ASDisplayNode *> *)subnodesForKey:(NSString *)key;
/** /**
* @abstract Subnodes that have been inserted in the layout transition * @abstract Subnodes that have been inserted in the layout transition

View File

@ -610,34 +610,43 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
{ {
return [self measureWithSizeRange:constrainedSize completion:^(ASLayout *pendingLayout, ASSizeRange constrainedSize) { return [self measureWithSizeRange:constrainedSize completion:^{
if ([[self class] usesImplicitHierarchyManagement]) { if ([[self class] usesImplicitHierarchyManagement]) {
[self __implicitlyInsertSubnodes]; [self __implicitlyInsertSubnodes];
[self __implicitlyRemoveSubnodes]; [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); ASDisplayNodeAssertThreadAffinity(self);
ASDN::MutexLocker l(_propertyLock); ASDN::MutexLocker l(_propertyLock);
if (![self __shouldSize]) if (![self __shouldSize])
return nil; return nil;
ASLayout *pendingLayout = _layout;
// only calculate the size if // only calculate the size if
// - we haven't already // - we haven't already
// - the constrained size range is different // - the constrained size range is different
if (!_flags.isMeasured || !ASSizeRangeEqualToSizeRange(constrainedSize, _constrainedSize)) { if (!_flags.isMeasured || !ASSizeRangeEqualToSizeRange(constrainedSize, _constrainedSize)) {
pendingLayout = [self calculateLayoutThatFits:constrainedSize]; _previousLayout = _layout;
[self __calculateSubnodeOperationsWithPendingLayout:pendingLayout currentLayout:_layout]; _layout = [self calculateLayoutThatFits:constrainedSize];
completion(pendingLayout, 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 - (ASLayout *)transitionLayoutWithAnimation:(BOOL)animated
@ -648,50 +657,41 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
- (ASLayout *)transitionLayoutWithSizeRange:(ASSizeRange)constrainedSize animated:(BOOL)animated - (ASLayout *)transitionLayoutWithSizeRange:(ASSizeRange)constrainedSize animated:(BOOL)animated
{ {
return [self measureWithSizeRange:constrainedSize completion:^(ASLayout *pendingLayout, ASSizeRange constrainedSize) { return [self measureWithSizeRange:constrainedSize completion:^{
_transitionContext = [[_ASTransitionContext alloc] initWithLayout:pendingLayout _transitionContext = [[_ASTransitionContext alloc] initWithAnimation:animated delegate:self];
constrainedSize:constrainedSize
animated:animated
delegate:self];
[self __implicitlyInsertSubnodes]; [self __implicitlyInsertSubnodes];
[self animateLayoutTransition:_transitionContext]; [self animateLayoutTransition:_transitionContext];
}]; }];
} }
- (void)__calculateSubnodeOperationsWithPendingLayout:(ASLayout *)pendingLayout currentLayout:(ASLayout *)currentLayout - (void)__calculateSubnodeOperationsWithLayout:(ASLayout *)layout previousLayout:(ASLayout *)previousLayout
{ {
if (_layout) { if (previousLayout) {
NSIndexSet *insertions, *deletions; NSIndexSet *insertions, *deletions;
[currentLayout.immediateSublayouts asdk_diffWithArray:pendingLayout.immediateSublayouts [previousLayout.immediateSublayouts asdk_diffWithArray:layout.immediateSublayouts
insertions:&insertions insertions:&insertions
deletions:&deletions deletions:&deletions
compareBlock:^BOOL(ASLayout *lhs, ASLayout *rhs) { compareBlock:^BOOL(ASLayout *lhs, ASLayout *rhs) {
return ASObjectIsEqual(lhs.layoutableObject, rhs.layoutableObject); return ASObjectIsEqual(lhs.layoutableObject, rhs.layoutableObject);
}]; }];
filterNodesInLayoutAtIndexes(pendingLayout, insertions, &_insertedSubnodes, &_insertedSubnodePositions); filterNodesInLayoutAtIndexes(layout, insertions, &_insertedSubnodes, &_insertedSubnodePositions);
filterNodesInLayoutAtIndexesWithIntersectingNodes(currentLayout, filterNodesInLayoutAtIndexesWithIntersectingNodes(previousLayout,
deletions, deletions,
_insertedSubnodes, _insertedSubnodes,
&_removedSubnodes, &_removedSubnodes,
&_removedSubnodePositions); &_removedSubnodePositions);
} else { } else {
NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [pendingLayout.immediateSublayouts count])]; NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [layout.immediateSublayouts count])];
filterNodesInLayoutAtIndexes(pendingLayout, indexes, &_insertedSubnodes, &_insertedSubnodePositions); filterNodesInLayoutAtIndexes(layout, indexes, &_insertedSubnodes, &_insertedSubnodePositions);
_removedSubnodes = nil; _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; _insertedSubnodes = nil;
_removedSubnodes = nil; _removedSubnodes = nil;
_previousLayout = nil;
[self calculatedLayoutDidChange]; [self calculatedLayoutDidChange];
// we generate placeholders at measureWithSizeRange: time so that a node is guaranteed // we generate placeholders at measureWithSizeRange: time so that a node is guaranteed
@ -777,7 +777,7 @@ static inline void filterNodesInLayoutAtIndexesWithIntersectingNodes(
- (void)didCompleteTransitionLayout:(id<ASContextTransitioning>)context - (void)didCompleteTransitionLayout:(id<ASContextTransitioning>)context
{ {
[self __implicitlyRemoveSubnodes]; [self __implicitlyRemoveSubnodes];
[self __applyLayout:context.layout constrainedSize:context.constrainedSize]; [self __completeLayoutCalculation];
} }
#pragma mark - Implicit node hierarchy managagment #pragma mark - Implicit node hierarchy managagment
@ -814,6 +814,27 @@ static inline void filterNodesInLayoutAtIndexesWithIntersectingNodes(
return _removedSubnodes; 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 - (void)transitionContext:(_ASTransitionContext *)context didComplete:(BOOL)didComplete
{ {
[self didCompleteTransitionLayout:context]; [self didCompleteTransitionLayout:context];

View File

@ -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 // This is the desired contentsScale, not the scale at which the layer's contents should be displayed
CGFloat _contentsScaleForDisplay; CGFloat _contentsScaleForDisplay;
ASLayout *_previousLayout;
ASLayout *_layout; ASLayout *_layout;
ASSizeRange _previousConstrainedSize;
ASSizeRange _constrainedSize; ASSizeRange _constrainedSize;
UIEdgeInsets _hitTestSlop; UIEdgeInsets _hitTestSlop;
NSMutableArray *_subnodes; NSMutableArray *_subnodes;

View File

@ -16,9 +16,13 @@
@protocol _ASTransitionContextDelegate <NSObject> @protocol _ASTransitionContextDelegate <NSObject>
- (NSArray<ASDisplayNode *> *)currentSubnodesWithTransitionContext:(_ASTransitionContext *)context; - (NSArray<ASDisplayNode *> *)currentSubnodesWithTransitionContext:(_ASTransitionContext *)context;
- (NSArray<ASDisplayNode *> *)insertedSubnodesWithTransitionContext:(_ASTransitionContext *)context; - (NSArray<ASDisplayNode *> *)insertedSubnodesWithTransitionContext:(_ASTransitionContext *)context;
- (NSArray<ASDisplayNode *> *)removedSubnodesWithTransitionContext:(_ASTransitionContext *)context; - (NSArray<ASDisplayNode *> *)removedSubnodesWithTransitionContext:(_ASTransitionContext *)context;
- (ASLayout *)transitionContext:(_ASTransitionContext *)context layoutForKey:(NSString *)key;
- (ASSizeRange)transitionContext:(_ASTransitionContext *)context constrainedSizeForKey:(NSString *)key;
- (void)transitionContext:(_ASTransitionContext *)context didComplete:(BOOL)didComplete; - (void)transitionContext:(_ASTransitionContext *)context didComplete:(BOOL)didComplete;
@end @end
@ -27,10 +31,6 @@
@property (assign, readonly, nonatomic, getter=isAnimated) BOOL animated; @property (assign, readonly, nonatomic, getter=isAnimated) BOOL animated;
@property (strong, readonly) ASLayout *layout; - (instancetype)initWithAnimation:(BOOL)animated delegate:(id<_ASTransitionContextDelegate>)delegate;
@property (assign, readonly) ASSizeRange constrainedSize;
- (instancetype)initWithLayout:(ASLayout *)layout constrainedSize:(ASSizeRange)constrainedSize animated:(BOOL)animated delegate:(id<_ASTransitionContextDelegate>)delegate;
@end @end

View File

@ -10,6 +10,10 @@
#import "ASLayout.h" #import "ASLayout.h"
NSString * const ASTransitionContextFromLayoutKey = @"org.asyncdisplaykit.ASTransitionContextFromLayoutKey";
NSString * const ASTransitionContextToLayoutKey = @"org.asyncdisplaykit.ASTransitionContextToLayoutKey";
@interface _ASTransitionContext () @interface _ASTransitionContext ()
@property (weak, nonatomic) id<_ASTransitionContextDelegate> delegate; @property (weak, nonatomic) id<_ASTransitionContextDelegate> delegate;
@ -18,12 +22,10 @@
@implementation _ASTransitionContext @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]; self = [super init];
if (self) { if (self) {
_layout = layout;
_constrainedSize = constrainedSize;
_animated = animated; _animated = animated;
_delegate = delegate; _delegate = delegate;
} }
@ -32,6 +34,16 @@
#pragma mark - ASContextTransitioning Protocol Implementation #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 - (CGRect)initialFrameForNode:(ASDisplayNode *)node
{ {
for (ASDisplayNode *subnode in [_delegate currentSubnodesWithTransitionContext:self]) { for (ASDisplayNode *subnode in [_delegate currentSubnodesWithTransitionContext:self]) {
@ -44,7 +56,7 @@
- (CGRect)finalFrameForNode:(ASDisplayNode *)node - (CGRect)finalFrameForNode:(ASDisplayNode *)node
{ {
for (ASLayout *layout in _layout.sublayouts) { for (ASLayout *layout in [self layoutForKey:ASTransitionContextToLayoutKey].sublayouts) {
if (layout.layoutableObject == node) { if (layout.layoutableObject == node) {
return [layout frame]; return [layout frame];
} }
@ -52,10 +64,10 @@
return CGRectZero; return CGRectZero;
} }
- (NSArray<ASDisplayNode *> *)subnodes - (NSArray<ASDisplayNode *> *)subnodesForKey:(NSString *)key
{ {
NSMutableArray<ASDisplayNode *> *subnodes = [NSMutableArray array]; NSMutableArray<ASDisplayNode *> *subnodes = [NSMutableArray array];
for (ASLayout *sublayout in _layout.immediateSublayouts) { for (ASLayout *sublayout in [self layoutForKey:key].immediateSublayouts) {
[subnodes addObject:(ASDisplayNode *)sublayout.layoutableObject]; [subnodes addObject:(ASDisplayNode *)sublayout.layoutableObject];
} }
return subnodes; return subnodes;