Perform animation transition on measureWithSizeRange instead of separate method

This commit is contained in:
Levi McCallum
2016-02-09 11:05:06 -08:00
parent 8238da8d00
commit d8d76635ff
7 changed files with 80 additions and 56 deletions

View File

@@ -15,6 +15,10 @@
*/ */
- (BOOL)isAnimated; - (BOOL)isAnimated;
- (ASLayout *)layout;
- (ASSizeRange)constrainedSize;
/** /**
@abstract The frame for the given node before the transition began. @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. @discussion Returns CGRectNull if the node was not in the hierarchy before the transition.

View File

@@ -6,6 +6,8 @@
* of patent rights can be found in the PATENTS file in the same directory. * of patent rights can be found in the PATENTS file in the same directory.
*/ */
#import "ASContextTransitioning.h"
@interface ASDisplayNode (Beta) @interface ASDisplayNode (Beta)
+ (BOOL)usesImplicitHierarchyManagement; + (BOOL)usesImplicitHierarchyManagement;
@@ -37,4 +39,32 @@
*/ */
@property (nonatomic, strong) ASDisplayNodeContextModifier didDisplayNodeContentWithRenderingContext; @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<ASContextTransitioning>)context;
/**
@discussion A place to clean up your nodes after the transition
*/
- (void)didCompleteLayoutTransition:(id<ASContextTransitioning>)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 @end

View File

@@ -12,7 +12,6 @@
#import <AsyncDisplayKit/ASAssert.h> #import <AsyncDisplayKit/ASAssert.h>
#import <AsyncDisplayKit/ASDisplayNode.h> #import <AsyncDisplayKit/ASDisplayNode.h>
#import <AsyncDisplayKit/ASThread.h> #import <AsyncDisplayKit/ASThread.h>
#import <AsyncDisplayKit/ASContextTransitioning.h>
@class ASLayoutSpec; @class ASLayoutSpec;
@@ -156,19 +155,6 @@ NS_ASSUME_NONNULL_BEGIN
- (void)invalidateCalculatedLayout; - (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<ASContextTransitioning>)context;
/**
@discussion A place to clean up your nodes after the transition
*/
- (void)didCompleteLayoutTransition:(id<ASContextTransitioning>)context;
/** @name Drawing */ /** @name Drawing */

View File

@@ -571,18 +571,6 @@ NS_ASSUME_NONNULL_BEGIN
@end @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. * Convenience methods for debugging.
*/ */

View File

@@ -644,6 +644,11 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
} }
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
{
return [self measureWithSizeRange:constrainedSize animated:NO];
}
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize animated:(BOOL)animated
{ {
ASDisplayNodeAssertThreadAffinity(self); ASDisplayNodeAssertThreadAffinity(self);
ASDN::MutexLocker l(_propertyLock); ASDN::MutexLocker l(_propertyLock);
@@ -651,11 +656,13 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
if (![self __shouldSize]) if (![self __shouldSize])
return nil; return nil;
ASLayout *newLayout;
// 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)) {
ASLayout *newLayout = [self calculateLayoutThatFits:constrainedSize]; newLayout = [self calculateLayoutThatFits:constrainedSize];
if ([[self class] usesImplicitHierarchyManagement]) { if ([[self class] usesImplicitHierarchyManagement]) {
if (_layout) { if (_layout) {
@@ -674,22 +681,37 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
_deletedSubnodes = nil; _deletedSubnodes = nil;
} }
if (!_deferImmediateHierarchyManagement) { if (animated) {
[self __transitionToLayout:newLayout constrainedSize:constrainedSize animated:animated];
} else {
[self __implicitlyInsertSubnodes]; [self __implicitlyInsertSubnodes];
[self __implicitlyRemoveSubnodes]; [self __implicitlyRemoveSubnodes];
[self __updateLayout:newLayout constrainedSize:constrainedSize];
} }
} else {
// 1.9.x code path
[self __updateLayout:newLayout constrainedSize:constrainedSize];
}
}
return newLayout;
} }
_layout = newLayout; - (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; _constrainedSize = constrainedSize;
_flags.isMeasured = YES; _flags.isMeasured = YES;
[self calculatedLayoutDidChange]; [self calculatedLayoutDidChange];
[self __primePlaceholder];
} }
ASDisplayNodeAssertTrue(_layout.layoutableObject == self); - (void)__primePlaceholder
ASDisplayNodeAssertTrue(_layout.size.width >= 0.0); {
ASDisplayNodeAssertTrue(_layout.size.height >= 0.0);
// we generate placeholders at measureWithSizeRange: time so that a node is guaranteed // 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 // 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] && if (self.placeholderEnabled && [self _displaysAsynchronously] &&
@@ -702,8 +724,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
[self _setupPlaceholderLayerContents]; [self _setupPlaceholderLayerContents];
} }
} }
return _layout;
} }
/** /**
@@ -1033,23 +1053,12 @@ static inline CATransform3D _calculateTransformFromReferenceToTarget(ASDisplayNo
#pragma mark - Layout Transition #pragma mark - Layout Transition
- (void)transitionLayoutWithAnimation:(BOOL)animated - (void)__transitionToLayout:(ASLayout *)layout constrainedSize:(ASSizeRange)constrainedSize animated:(BOOL)animated
{ {
[self transitionLayoutThatFits:_constrainedSize animated:animated]; _transitionContext = [[_ASTransitionContext alloc] initWithLayout:layout
} constrainedSize:constrainedSize
animated:animated
- (void)transitionLayoutThatFits:(ASSizeRange)constrainedSize animated:(BOOL)animated delegate:self];
{
[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];
[self __implicitlyInsertSubnodes]; [self __implicitlyInsertSubnodes];
[self animateLayoutTransition:_transitionContext]; [self animateLayoutTransition:_transitionContext];
} }
@@ -1063,6 +1072,7 @@ static inline CATransform3D _calculateTransformFromReferenceToTarget(ASDisplayNo
- (void)didCompleteTransitionLayout:(id<ASContextTransitioning>)context - (void)didCompleteTransitionLayout:(id<ASContextTransitioning>)context
{ {
[self __implicitlyRemoveSubnodes]; [self __implicitlyRemoveSubnodes];
[self __updateLayout:context.layout constrainedSize:context.constrainedSize];
} }
#pragma mark - Implicit node hierarchy managagment #pragma mark - Implicit node hierarchy managagment

View File

@@ -25,6 +25,10 @@
@property (assign, readonly, nonatomic, getter=isAnimated) BOOL animated; @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 @end

View File

@@ -16,10 +16,12 @@
@implementation _ASTransitionContext @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]; self = [super init];
if (self) { if (self) {
_layout = layout;
_constrainedSize = constrainedSize;
_animated = animated; _animated = animated;
_delegate = delegate; _delegate = delegate;
} }