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
TextureGroup#408)

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 authored and bernieperez committed Apr 25, 2018
1 parent 1640b84 commit f8474c0
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 f8474c0

Please sign in to comment.