diff --git a/AsyncDisplayKit/ASContextTransitioning.h b/AsyncDisplayKit/ASContextTransitioning.h index 1b87cad643..f013ff2134 100644 --- a/AsyncDisplayKit/ASContextTransitioning.h +++ b/AsyncDisplayKit/ASContextTransitioning.h @@ -10,7 +10,10 @@ // of patent rights can be found in the PATENTS file in the same directory. // -#import +#import + +@class ASDisplayNode; +@class ASLayout; NS_ASSUME_NONNULL_BEGIN diff --git a/AsyncDisplayKit/ASDisplayNode+Beta.h b/AsyncDisplayKit/ASDisplayNode+Beta.h index b44715c97e..aa3b3d96a1 100644 --- a/AsyncDisplayKit/ASDisplayNode+Beta.h +++ b/AsyncDisplayKit/ASDisplayNode+Beta.h @@ -8,7 +8,7 @@ // of patent rights can be found in the PATENTS file in the same directory. // -#import "ASContextTransitioning.h" +#import "ASDisplayNode.h" #import "ASLayoutRangeType.h" NS_ASSUME_NONNULL_BEGIN @@ -65,63 +65,12 @@ ASDISPLAYNODE_EXTERN_C_END @property (nonatomic) BOOL usesImplicitHierarchyManagement; -/** - * @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 Transitions the current layout with a new constrained size. Must be called on main thread. - * - * @param animated Animation is optional, but will still proceed through your `animateLayoutTransition` implementation with `isAnimated == NO`. - * - * @param shouldMeasureAsync Measure the layout asynchronously. - * - * @param measurementCompletion Optional completion block called only if a new layout is calculated. - * It is called on main, right after the measurement and before -animateLayoutTransition:. - * - * @discussion If the passed constrainedSize is the the same as the node's current constrained size, this method is noop. - * - * @see animateLayoutTransition: - */ -- (void)transitionLayoutWithSizeRange:(ASSizeRange)constrainedSize - animated:(BOOL)animated - shouldMeasureAsync:(BOOL)shouldMeasureAsync - measurementCompletion:(nullable void(^)())completion; - -/** - * @abstract Invalidates the current layout and begins a relayout of the node with the current `constrainedSize`. Must be called on main thread. - * - * @param animated Animation is optional, but will still proceed through your `animateLayoutTransition` implementation with `isAnimated == NO`. - * - * @param shouldMeasureAsync Measure the layout asynchronously. - * - * @param measurementCompletion Optional completion block called only if a new layout is calculated. - * It is called right after the measurement and before -animateLayoutTransition:. - * - * @see animateLayoutTransition: - */ -- (void)transitionLayoutWithAnimation:(BOOL)animated - shouldMeasureAsync:(BOOL)shouldMeasureAsync - measurementCompletion:(nullable void(^)())completion; - - /** * @abstract Currently used by ASNetworkImageNode and ASMultiplexImageNode to allow their placeholders to stay if they are loading an image from the network. * Otherwise, a display pass is scheduled and completes, but does not actually draw anything - and ASDisplayNode considers the element finished. */ - (BOOL)placeholderShouldPersist; -/** - * @abstract Cancels all performing layout transitions. Can be called on any thread. - */ -- (void)cancelLayoutTransitionsInProgress; - /** * @abstract Indicates that the receiver and all subnodes have finished displaying. May be called more than once, for example if the receiver has * a network image node. This is called after the first display pass even if network image nodes have not downloaded anything (text would be done, diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index 3dd0f2749c..08b2bfe469 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -16,6 +16,7 @@ #import #import #import +#import #define ASDisplayNodeLoggingEnabled 0 @@ -744,6 +745,54 @@ NS_ASSUME_NONNULL_BEGIN // Accessibility identification support @property (nonatomic, copy, nullable) NSString *accessibilityIdentifier; +@end + +@interface ASDisplayNode (LayoutTransitioning) + +/** + * @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:(nonnull id)context; + +/** + * @discussion A place to clean up your nodes after the transition + */ +- (void)didCompleteLayoutTransition:(nonnull id)context; + +/** + * @abstract Transitions the current layout with a new constrained size. Must be called on main thread. + * + * @param animated Animation is optional, but will still proceed through your `animateLayoutTransition` implementation with `isAnimated == NO`. + * @param shouldMeasureAsync Measure the layout asynchronously. + * @param measurementCompletion Optional completion block called only if a new layout is calculated. + * + * @discussion It is called on main, right after the measurement and before -animateLayoutTransition:. If the passed constrainedSize is the the same as the node's current constrained size, this method is noop. + * + * @see animateLayoutTransition: + */ +- (void)transitionLayoutWithSizeRange:(ASSizeRange)constrainedSize + animated:(BOOL)animated + measurementCompletion:(nullable void(^)())completion; + +/** + * @abstract Invalidates the current layout and begins a relayout of the node with the current `constrainedSize`. Must be called on main thread. + * + * @discussion It is called right after the measurement and before -animateLayoutTransition:. + * + * @param animated Animation is optional, but will still proceed through your `animateLayoutTransition` implementation with `isAnimated == NO`. + * @param measurementCompletion Optional completion block called only if a new layout is calculated. + * + * @see animateLayoutTransition: + * + */ +- (void)transitionLayoutAnimated:(BOOL)animated measurementCompletion:(nullable void(^)())completion; + +/** + * @abstract Cancels all performing layout transitions. Can be called on any thread. + */ +- (void)cancelLayoutTransition; + + @end /* @@ -776,6 +825,54 @@ NS_ASSUME_NONNULL_BEGIN @interface ASDisplayNode (Deprecated) +/** + * @abstract Transitions the current layout with a new constrained size. Must be called on main thread. + * + * @param animated Animation is optional, but will still proceed through your `animateLayoutTransition` implementation with `isAnimated == NO`. + * @param shouldMeasureAsync Measure the layout asynchronously. + * @param measurementCompletion Optional completion block called only if a new layout is calculated. + * It is called on main, right after the measurement and before -animateLayoutTransition:. + * + * @discussion If the passed constrainedSize is the the same as the node's current constrained size, this method is noop. + * + * @see animateLayoutTransition: + * + * @deprecated Deprecated in version 2.0: Use transitionLayoutWithSizeRange:animated:measurementCompletion:. + * shouldMeasureAsync is enabled by default now. + * + */ +- (void)transitionLayoutWithSizeRange:(ASSizeRange)constrainedSize + animated:(BOOL)animated + shouldMeasureAsync:(BOOL)shouldMeasureAsync + measurementCompletion:(nullable void(^)())completion ASDISPLAYNODE_DEPRECATED; + + +/** + * @abstract Invalidates the current layout and begins a relayout of the node with the current `constrainedSize`. Must be called on main thread. + * + * @discussion It is called right after the measurement and before -animateLayoutTransition:. + * + * @param animated Animation is optional, but will still proceed through your `animateLayoutTransition` implementation with `isAnimated == NO`. + * @param shouldMeasureAsync Measure the layout asynchronously. + * @param measurementCompletion Optional completion block called only if a new layout is calculated. + * + * @see animateLayoutTransition: + * + * @deprecated Deprecated in version 2.0: Use transitionLayoutAnimated:measurementCompletion: + * shouldMeasureAsync is enabled by default now. + * + */ +- (void)transitionLayoutWithAnimation:(BOOL)animated + shouldMeasureAsync:(BOOL)shouldMeasureAsync + measurementCompletion:(nullable void(^)())completion ASDISPLAYNODE_DEPRECATED; + +/** + * @abstract Cancels all performing layout transitions. Can be called on any thread. + * + * @deprecated Deprecated in version 2.0: Use cancelLayoutTransition + */ +- (void)cancelLayoutTransitionsInProgress ASDISPLAYNODE_DEPRECATED; + - (void)reclaimMemory ASDISPLAYNODE_DEPRECATED; - (void)recursivelyReclaimMemory ASDISPLAYNODE_DEPRECATED; @property (nonatomic, assign) BOOL placeholderFadesOut ASDISPLAYNODE_DEPRECATED; diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index edbf245461..f4b804d6d7 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -646,7 +646,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) return _calculatedLayout ? : [ASLayout layoutWithLayoutableObject:self constrainedSizeRange:constrainedSize size:CGSizeZero]; } - [self cancelLayoutTransitionsInProgress]; + [self cancelLayoutTransition]; ASLayout *previousLayout = _calculatedLayout; ASLayout *newLayout = [self calculateLayoutThatFits:constrainedSize]; @@ -701,9 +701,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) #pragma mark - Layout Transition -- (void)transitionLayoutWithAnimation:(BOOL)animated - shouldMeasureAsync:(BOOL)shouldMeasureAsync - measurementCompletion:(void(^)())completion +- (void)transitionLayoutAnimated:(BOOL)animated measurementCompletion:(void (^)())completion { if (_calculatedLayout == nil) { // constrainedSizeRange returns a struct and is invalid to call on nil. @@ -714,14 +712,12 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) [self invalidateCalculatedLayout]; [self transitionLayoutWithSizeRange:_calculatedLayout.constrainedSizeRange animated:animated - shouldMeasureAsync:shouldMeasureAsync measurementCompletion:completion]; } - (void)transitionLayoutWithSizeRange:(ASSizeRange)constrainedSize animated:(BOOL)animated - shouldMeasureAsync:(BOOL)shouldMeasureAsync - measurementCompletion:(void(^)())completion + measurementCompletion:(void (^)())completion { ASDisplayNodeAssertMainThread(); if (! [self shouldMeasureWithSizeRange:constrainedSize]) { @@ -741,7 +737,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) node.pendingTransitionID = transitionID; }); - void (^transitionBlock)() = ^{ + ASPerformBlockOnBackgroundThread(^{ if ([self _shouldAbortTransitionWithID:transitionID]) { return; } @@ -803,12 +799,10 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) // Kick off animating the layout transition [self animateLayoutTransition:_pendingLayoutTransitionContext]; }); - }; - - ASPerformBlockOnBackgroundThread(transitionBlock); + }); } -- (void)cancelLayoutTransitionsInProgress +- (void)cancelLayoutTransition { ASDN::MutexLocker l(__instanceLock__); if ([self _isTransitionInProgress]) { @@ -861,7 +855,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) return (!_transitionInProgress || _transitionID != transitionID); } -#pragma mark - Layout Transition API / ASDisplayNode (Beta) +#pragma mark Layout Transition API /* * Hook for subclasse to perform an animation based on the given ASContextTransitioning. By default this just layouts @@ -882,7 +876,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) [_pendingLayoutTransition applySubnodeRemovals]; } -#pragma mark - _ASTransitionContextCompletionDelegate +#pragma mark _ASTransitionContextCompletionDelegate /* * After completeTransition: is called on the ASContextTransitioning object in animateLayoutTransition: this @@ -3172,6 +3166,26 @@ static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode"; @implementation ASDisplayNode (Deprecated) +- (void)transitionLayoutWithAnimation:(BOOL)animated + shouldMeasureAsync:(BOOL)shouldMeasureAsync + measurementCompletion:(void(^)())completion +{ + [self transitionLayoutAnimated:animated measurementCompletion:completion]; +} + +- (void)transitionLayoutWithSizeRange:(ASSizeRange)constrainedSize + animated:(BOOL)animated + shouldMeasureAsync:(BOOL)shouldMeasureAsync + measurementCompletion:(void(^)())completion +{ + [self transitionLayoutWithSizeRange:constrainedSize animated:animated measurementCompletion:completion]; +} + +- (void)cancelLayoutTransitionsInProgress +{ + [self cancelLayoutTransition]; +} + - (void)setPlaceholderFadesOut:(BOOL)placeholderFadesOut { self.placeholderFadeDuration = placeholderFadesOut ? 0.1 : 0.0; diff --git a/AsyncDisplayKit/_ASTransitionContext.m b/AsyncDisplayKit/_ASTransitionContext.m index e8ad4e8c14..031810400c 100644 --- a/AsyncDisplayKit/_ASTransitionContext.m +++ b/AsyncDisplayKit/_ASTransitionContext.m @@ -11,7 +11,7 @@ // #import "_ASTransitionContext.h" - +#import "ASDisplayNode.h" #import "ASLayout.h" diff --git a/AsyncDisplayKitTests/ASDisplayNodeImplicitHierarchyTests.m b/AsyncDisplayKitTests/ASDisplayNodeImplicitHierarchyTests.m index 627960d12d..95adca7a35 100644 --- a/AsyncDisplayKitTests/ASDisplayNodeImplicitHierarchyTests.m +++ b/AsyncDisplayKitTests/ASDisplayNodeImplicitHierarchyTests.m @@ -199,7 +199,7 @@ node.layoutState = @2; [node invalidateCalculatedLayout]; - [node transitionLayoutWithAnimation:YES shouldMeasureAsync:YES measurementCompletion:^{ + [node transitionLayoutAnimated:YES measurementCompletion:^{ // Push this to the next runloop to let async insertion / removing of nodes finished before checking dispatch_async(dispatch_get_main_queue(), ^{ XCTAssertEqual(node.subnodes[0], node2);