Address another round of comments

This commit is contained in:
Michael Schneider 2016-11-18 09:50:49 -08:00
parent fc747ca58a
commit f9c70470d8
5 changed files with 39 additions and 28 deletions

View File

@ -118,7 +118,7 @@ static NSMutableSet *__cellClassesForVisibilityNotifications = nil; // See +init
_viewControllerNode.frame = self.bounds; _viewControllerNode.frame = self.bounds;
} }
- (void)displayNodeDidInvalidateSizeNewSize:(CGSize)newSize - (void)_locked_displayNodeDidInvalidateSizeNewSize:(CGSize)newSize
{ {
CGSize oldSize = self.bounds.size; CGSize oldSize = self.bounds.size;
if (CGSizeEqualToSize(oldSize, newSize) == NO) { if (CGSizeEqualToSize(oldSize, newSize) == NO) {

View File

@ -796,7 +796,7 @@ extern NSInteger const ASDefaultDrawingPriority;
/** /**
* @abstract Invalidates the current layout and begins a relayout of the node with the current `constrainedSize`. Must be called on main thread. * @abstract Invalidates the 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:. * @discussion It is called right after the measurement and before -animateLayoutTransition:.
* *

View File

@ -735,7 +735,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
// Perform a measurement pass to get the current layout // Perform a measurement pass to get the current layout
// It's important to differentiate between layout and measure pass here. Calling `layoutThatFits:` just perform a // It's important to differentiate between layout and measure pass here. Calling `layoutThatFits:` just perform a
// measure pass and no layout pass immediately. If a layout pass wold be forced via `layoutIfNeeded` it could cause an // measure pass and no layout pass immediately. If a layout pass would be forced via `layoutIfNeeded` it could cause an
// infinite loop as in `__layout` we check if the size changed and we are just to inform the node that the size changed // infinite loop as in `__layout` we check if the size changed and we are just to inform the node that the size changed
ASLayout *layout = [self layoutThatFits:constrainedSize]; ASLayout *layout = [self layoutThatFits:constrainedSize];
@ -743,13 +743,13 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
if (CGSizeEqualToSize(oldSize, layout.size) == NO) { if (CGSizeEqualToSize(oldSize, layout.size) == NO) {
// If the size of the layout changes inform our container (e.g ASTableView, ASCollectionView, ASViewController, ...) // If the size of the layout changes inform our container (e.g ASTableView, ASCollectionView, ASViewController, ...)
// that we need it to change our bounds size. // that we need it to change our bounds size.
[self displayNodeDidInvalidateSizeNewSize:layout.size]; [self _locked_displayNodeDidInvalidateSizeNewSize:layout.size];
} }
__instanceLock__.unlock(); __instanceLock__.unlock();
} }
- (void)displayNodeDidInvalidateSizeNewSize:(CGSize)size - (void)_locked_displayNodeDidInvalidateSizeNewSize:(CGSize)size
{ {
ASDisplayNodeAssertThreadAffinity(self); ASDisplayNodeAssertThreadAffinity(self);
@ -829,14 +829,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
shouldMeasureAsync:(BOOL)shouldMeasureAsync shouldMeasureAsync:(BOOL)shouldMeasureAsync
measurementCompletion:(void(^)())completion measurementCompletion:(void(^)())completion
{ {
if (_calculatedDisplayNodeLayout->layout == nil) {
// No measure pass happened before, it's not possible to reuse the constrained size for the transition
// Using CGSizeZero for the sizeRange can cause negative values in client layout code.
return;
}
[self setNeedsLayout]; [self setNeedsLayout];
[self transitionLayoutWithSizeRange:_calculatedDisplayNodeLayout->constrainedSize [self transitionLayoutWithSizeRange:[self _locked_constrainedSizeForLayoutPass]
animated:animated animated:animated
shouldMeasureAsync:shouldMeasureAsync shouldMeasureAsync:shouldMeasureAsync
measurementCompletion:completion]; measurementCompletion:completion];
@ -848,9 +842,11 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
shouldMeasureAsync:(BOOL)shouldMeasureAsync shouldMeasureAsync:(BOOL)shouldMeasureAsync
measurementCompletion:(void(^)())completion measurementCompletion:(void(^)())completion
{ {
// Passed constrainedSize is the the same as the node's current constrained size it's a noop
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
if (_calculatedDisplayNodeLayout->isValidForConstrainedSizeParentSize(constrainedSize, constrainedSize.max)) {
// Check if it's a subnode in a layout transition. In this case no measurement is needed as it's part of
// the layout transition
if ([self _isInvolvedInLayoutTransition]) {
return; return;
} }
@ -859,20 +855,23 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
ASDisplayNodeAssert(ASHierarchyStateIncludesLayoutPending(_hierarchyState) == NO, @"Can't start a transition when one of the supernodes is performing one."); ASDisplayNodeAssert(ASHierarchyStateIncludesLayoutPending(_hierarchyState) == NO, @"Can't start a transition when one of the supernodes is performing one.");
} }
// Every new layout transition has a transition id associated to check in subsequent transitions for cancelling
int32_t transitionID = [self _startNewTransition]; int32_t transitionID = [self _startNewTransition];
// Move all subnodes in a pending state // Move all subnodes in layout pending state for this transition
ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) { ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) {
ASDisplayNodeAssert([node _isTransitionInProgress] == NO, @"Can't start a transition when one of the subnodes is performing one."); ASDisplayNodeAssert([node _isTransitionInProgress] == NO, @"Can't start a transition when one of the subnodes is performing one.");
node.hierarchyState |= ASHierarchyStateLayoutPending; node.hierarchyState |= ASHierarchyStateLayoutPending;
node.pendingTransitionID = transitionID; node.pendingTransitionID = transitionID;
}); });
// Transition block that executes the layout transition
void (^transitionBlock)(void) = ^{ void (^transitionBlock)(void) = ^{
if ([self _shouldAbortTransitionWithID:transitionID]) { if ([self _shouldAbortTransitionWithID:transitionID]) {
return; return;
} }
// Perform a full layout creation pass with passed in constrained size to create the new layout for the transition
ASLayout *newLayout; ASLayout *newLayout;
{ {
ASDN::MutexLocker l(__instanceLock__); ASDN::MutexLocker l(__instanceLock__);
@ -905,7 +904,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
return; return;
} }
// Update display node layout // Update calculated layout
auto previousLayout = _calculatedDisplayNodeLayout; auto previousLayout = _calculatedDisplayNodeLayout;
auto pendingLayout = std::make_shared<ASDisplayNodeLayout>( auto pendingLayout = std::make_shared<ASDisplayNodeLayout>(
newLayout, newLayout,
@ -944,6 +943,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
}); });
}; };
// Start transition based on flag on current or background thread
if (shouldMeasureAsync) { if (shouldMeasureAsync) {
ASPerformBlockOnBackgroundThread(transitionBlock); ASPerformBlockOnBackgroundThread(transitionBlock);
} else { } else {
@ -971,6 +971,18 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
return _transitionInProgress; return _transitionInProgress;
} }
- (BOOL)_isInvolvedInLayoutTransition
{
ASDN::MutexLocker l(__instanceLock__);
if (ASHierarchyStateIncludesLayoutPending(_hierarchyState)) {
ASLayoutElementContext context = ASLayoutElementGetCurrentContext();
if (ASLayoutElementContextIsNull(context) || _pendingTransitionID != context.transitionID) {
return YES;
}
}
return NO;
}
/// Starts a new transition and returns the transition id /// Starts a new transition and returns the transition id
- (int32_t)_startNewTransition - (int32_t)_startNewTransition
{ {
@ -1463,12 +1475,9 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
{ {
// Check if it's a subnode in a layout transition. In this case no measurement is needed as it's part of // Check if it's a subnode in a layout transition. In this case no measurement is needed as it's part of
// the layout transition // the layout transition
if (ASHierarchyStateIncludesLayoutPending(_hierarchyState)) { if ([self _isInvolvedInLayoutTransition]) {
ASLayoutElementContext context = ASLayoutElementGetCurrentContext();
if (ASLayoutElementContextIsNull(context) || _pendingTransitionID != context.transitionID) {
return; return;
} }
}
// Check if we can reuse the calculated display node layout. We prefer the _pendingDisplayNodeLayout over the // Check if we can reuse the calculated display node layout. We prefer the _pendingDisplayNodeLayout over the
// _calculatedDisplayNodeLayout though // _calculatedDisplayNodeLayout though
@ -1486,13 +1495,12 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
BOOL didCreateNewContext = NO; BOOL didCreateNewContext = NO;
BOOL didOverrideExistingContext = NO; BOOL didOverrideExistingContext = NO;
BOOL shouldVisualizeLayout = ASHierarchyStateIncludesVisualizeLayout(_hierarchyState); BOOL shouldVisualizeLayout = ASHierarchyStateIncludesVisualizeLayout(_hierarchyState);
ASLayoutElementContext context; ASLayoutElementContext context = ASLayoutElementGetCurrentContext();
if (ASLayoutElementContextIsNull(ASLayoutElementGetCurrentContext())) { if (ASLayoutElementContextIsNull(context)) {
context = ASLayoutElementContextMake(ASLayoutElementContextDefaultTransitionID, shouldVisualizeLayout); context = ASLayoutElementContextMake(ASLayoutElementContextDefaultTransitionID, shouldVisualizeLayout);
ASLayoutElementSetCurrentContext(context); ASLayoutElementSetCurrentContext(context);
didCreateNewContext = YES; didCreateNewContext = YES;
} else { } else {
context = ASLayoutElementGetCurrentContext();
if (context.needsVisualizeNode != shouldVisualizeLayout) { if (context.needsVisualizeNode != shouldVisualizeLayout) {
context.needsVisualizeNode = shouldVisualizeLayout; context.needsVisualizeNode = shouldVisualizeLayout;
ASLayoutElementSetCurrentContext(context); ASLayoutElementSetCurrentContext(context);
@ -1500,7 +1508,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
} }
} }
// Figure out previos and pending layout for layout transition // Figure out previous and pending layouts for layout transition
auto previousLayout = _calculatedDisplayNodeLayout; auto previousLayout = _calculatedDisplayNodeLayout;
auto pendingLayout = [=]() -> std::shared_ptr<ASDisplayNodeLayout> { auto pendingLayout = [=]() -> std::shared_ptr<ASDisplayNodeLayout> {
// Check if the pending display node layout can be used to transition to // Check if the pending display node layout can be used to transition to

View File

@ -187,7 +187,7 @@ __unused static NSString * _Nonnull NSStringFromASHierarchyState(ASHierarchyStat
* @abstract Subclass hook for nodes that are acting as root nodes. This method is called if one of the subnodes * @abstract Subclass hook for nodes that are acting as root nodes. This method is called if one of the subnodes
* size is invalidated and may need to result in a different size as the current calculated size. * size is invalidated and may need to result in a different size as the current calculated size.
*/ */
- (void)displayNodeDidInvalidateSizeNewSize:(CGSize)newSize; - (void)_locked_displayNodeDidInvalidateSizeNewSize:(CGSize)newSize;
@end @end

View File

@ -44,11 +44,14 @@ BOOL ASDisplayNodeRunRunLoopUntilBlockIsTrue(as_condition_block_t block)
return passed; return passed;
} }
void ASDisplayNodeSizeToFitSize(ASDisplayNode *node, CGSize size) { void ASDisplayNodeSizeToFitSize(ASDisplayNode *node, CGSize size)
{
CGSize sizeThatFits = [node layoutThatFits:ASSizeRangeMake(size)].size; CGSize sizeThatFits = [node layoutThatFits:ASSizeRangeMake(size)].size;
node.bounds = (CGRect){.origin = CGPointZero, .size = sizeThatFits}; node.bounds = (CGRect){.origin = CGPointZero, .size = sizeThatFits};
} }
void ASDisplayNodeSizeToFitSizeRange(ASDisplayNode *node, ASSizeRange sizeRange) {
void ASDisplayNodeSizeToFitSizeRange(ASDisplayNode *node, ASSizeRange sizeRange)
{
CGSize sizeThatFits = [node layoutThatFits:sizeRange].size; CGSize sizeThatFits = [node layoutThatFits:sizeRange].size;
node.bounds = (CGRect){.origin = CGPointZero, .size = sizeThatFits}; node.bounds = (CGRect){.origin = CGPointZero, .size = sizeThatFits};
} }