Skip to content

Commit

Permalink
[Yoga] Refine the handling of measurement functions when Yoga is used.
Browse files Browse the repository at this point in the history
This ensures that measure funcs are not set for container / empty spacer
nodes, because Yoga has more internal capabilities than layoutThatFits:
knows about.

Avoid need for the main layout pass to directly call setup for measure
funcs, by making it an implicit part of .yogaLayoutInProgress =.

Tear down the measure func after the layout pass to avoid retain cycle.
  • Loading branch information
appleguy committed Jul 2, 2017
1 parent fcb293e commit e0c1b19
Show file tree
Hide file tree
Showing 2 changed files with 11 additions and 16 deletions.
2 changes: 1 addition & 1 deletion Source/ASDisplayNode+Layout.mm
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ - (NSString *)asciiArtName
{
NSString *string = NSStringFromClass([self class]);
if (_debugName) {
string = [string stringByAppendingString:[NSString stringWithFormat:@"\"%@\"", _debugName]];
string = [string stringByAppendingString:[NSString stringWithFormat:@"\"%@\"",_debugName]];
}
return string;
}
Expand Down
25 changes: 10 additions & 15 deletions Source/ASDisplayNode+Yoga.mm
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ @implementation ASDisplayNode (Yoga)

- (void)setYogaChildren:(NSArray *)yogaChildren
{
for (ASDisplayNode *child in _yogaChildren) {
for (ASDisplayNode *child in [_yogaChildren copy]) {
// Make sure to un-associate the YGNodeRef tree before replacing _yogaChildren
// If this becomes a performance bottleneck, it can be optimized by not doing the NSArray removals here.
[self removeYogaChild:child];
Expand Down Expand Up @@ -68,14 +68,8 @@ - (void)addYogaChild:(ASDisplayNode *)child
// Clean up state in case this child had another parent.
[self removeYogaChild:child];

BOOL hadZeroChildren = (_yogaChildren.count == 0);

[_yogaChildren addObject:child];

// Ensure any measure function is removed before inserting the YGNodeRef child.
if (hadZeroChildren) {
[self updateYogaMeasureFuncIfNeeded];
}
// YGNodeRef insertion is done in setParent:
child.yogaParent = self;
}
Expand All @@ -86,15 +80,10 @@ - (void)removeYogaChild:(ASDisplayNode *)child
return;
}

BOOL hadChildren = (_yogaChildren.count > 0);
[_yogaChildren removeObjectIdenticalTo:child];

// YGNodeRef removal is done in setParent:
child.yogaParent = nil;
// Ensure any measure function is re-added after removing the YGNodeRef child.
if (hadChildren && _yogaChildren.count == 0) {
[self updateYogaMeasureFuncIfNeeded];
}
}

- (void)semanticContentAttributeDidChange:(UISemanticContentAttribute)attribute
Expand Down Expand Up @@ -144,6 +133,7 @@ - (ASLayout *)yogaCalculatedLayout
- (void)setYogaLayoutInProgress:(BOOL)yogaLayoutInProgress
{
setFlag(YogaLayoutInProgress, yogaLayoutInProgress);
[self updateYogaMeasureFuncIfNeeded];
}

- (BOOL)yogaLayoutInProgress
Expand Down Expand Up @@ -184,8 +174,14 @@ - (void)updateYogaMeasureFuncIfNeeded
{
// Size calculation via calculateSizeThatFits: or layoutSpecThatFits:
// This will be used for ASTextNode, as well as any other node that has no Yoga children
id <ASLayoutElement> layoutElementToMeasure = (self.yogaChildren.count == 0 ? self : nil);
ASLayoutElementYogaUpdateMeasureFunc(self.style.yogaNode, layoutElementToMeasure);
BOOL isLeafNode = (self.yogaChildren.count == 0);
BOOL definesCustomLayout = [self implementsLayoutMethod];

// We set the measure func only during layout. Otherwise, a cycle is created:
// The YGNodeRef Context will retain the ASDisplayNode, which retains the style, which owns the YGNodeRef.
BOOL shouldHaveMeasureFunc = (isLeafNode && definesCustomLayout && checkFlag(YogaLayoutInProgress));

ASLayoutElementYogaUpdateMeasureFunc(self.style.yogaNode, shouldHaveMeasureFunc ? self : nil);
}

- (void)invalidateCalculatedYogaLayout
Expand Down Expand Up @@ -214,7 +210,6 @@ - (void)calculateLayoutFromYogaRoot:(ASSizeRange)rootConstrainedSize
// Prepare all children for the layout pass with the current Yoga tree configuration.
ASDisplayNodePerformBlockOnEveryYogaChild(self, ^(ASDisplayNode * _Nonnull node) {
node.yogaLayoutInProgress = YES;
[node updateYogaMeasureFuncIfNeeded];
});

if (ASSizeRangeEqualToSizeRange(rootConstrainedSize, ASSizeRangeUnconstrained)) {
Expand Down

0 comments on commit e0c1b19

Please sign in to comment.