diff --git a/AsyncDisplayKit/ASDisplayNode+Beta.h b/AsyncDisplayKit/ASDisplayNode+Beta.h index 11cd6d0e70..4bb108b66a 100644 --- a/AsyncDisplayKit/ASDisplayNode+Beta.h +++ b/AsyncDisplayKit/ASDisplayNode+Beta.h @@ -98,4 +98,9 @@ ASDISPLAYNODE_EXTERN_C_END */ - (BOOL)placeholderShouldPersist; +/** + * @abstract Cancels all performing layout transitions. Can be called on any thread. + */ +- (void)cancelLayoutTransitionsInProgress; + @end diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 0596eaf510..f1b6aa26f2 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -593,14 +593,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) return _layout; } - if ([self _hasTransitionsInProgress]) { - // Invalidate transition sentinel to cancel transitions in progress - [self _invalidateTransitionSentinel]; - // Tell subnodes to exit layout pending state and clear related properties - ASDisplayNodePerformBlockOnEverySubnode(self, ^(ASDisplayNode * _Nonnull node) { - node.hierarchyState &= (~ASHierarchyStateLayoutPending); - }); - } + [self cancelLayoutTransitionsInProgress]; ASLayout *previousLayout = _layout; ASSizeRange previousConstrainedSize = _constrainedSize; @@ -698,12 +691,14 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) } ASPerformBlockOnMainThread(^{ + // Grab _propertyLock here to make sure this transition isn't invalidated + // right after it passed the validation test and before it proceeds + ASDN::MutexLocker l(_propertyLock); + if ([self _shouldAbortTransitionWithID:transitionID]) { return; } - ASDN::MutexLocker l(_propertyLock); - ASLayout *previousLayout = _layout; ASSizeRange previousConstrainedSize = _constrainedSize; [self applyLayout:newLayout constrainedSize:constrainedSize layoutContext:nil]; @@ -769,6 +764,19 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) // subclass override } +- (void)cancelLayoutTransitionsInProgress +{ + ASDN::MutexLocker l(_propertyLock); + if ([self _hasTransitionsInProgress]) { + // Invalidate transition sentinel to cancel transitions in progress + [self _invalidateTransitionSentinel]; + // Tell subnodes to exit layout pending state and clear related properties + ASDisplayNodePerformBlockOnEverySubnode(self, ^(ASDisplayNode * _Nonnull node) { + node.hierarchyState &= (~ASHierarchyStateLayoutPending); + }); + } +} + #pragma mark - Layout Transition - (BOOL)usesImplicitHierarchyManagement