Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Yoga] Minimize number of nodes that have MeasureFunc set on them. #369

Merged
merged 1 commit into from
Jun 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions Source/ASDisplayNode+Layout.mm
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ @implementation ASDisplayNode (ASLayoutElement)

#pragma mark <ASLayoutElement>

- (BOOL)implementsLayoutMethod
{
ASDN::MutexLocker l(__instanceLock__);
return (_methodOverrides & (ASDisplayNodeMethodOverrideLayoutSpecThatFits |
ASDisplayNodeMethodOverrideCalcLayoutThatFits |
ASDisplayNodeMethodOverrideCalcSizeThatFits)) != 0 || _layoutSpecBlock != nil;
}


- (ASLayoutElementStyle *)style
{
ASDN::MutexLocker l(__instanceLock__);
Expand Down Expand Up @@ -148,9 +157,9 @@ @implementation ASDisplayNode (ASLayout)

- (void)setLayoutSpecBlock:(ASLayoutSpecBlock)layoutSpecBlock
{
// For now there should never be an override of layoutSpecThatFits: / layoutElementThatFits: and a layoutSpecBlock
ASDisplayNodeAssert(!(_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits), @"Overwriting layoutSpecThatFits: and providing a layoutSpecBlock block is currently not supported");

// For now there should never be an override of layoutSpecThatFits: and a layoutSpecBlock together.
ASDisplayNodeAssert(!(_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits),
@"Nodes with a .layoutSpecBlock must not also implement -layoutSpecThatFits:");
ASDN::MutexLocker l(__instanceLock__);
_layoutSpecBlock = layoutSpecBlock;
}
Expand Down
9 changes: 9 additions & 0 deletions Source/ASDisplayNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,15 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
if (ASDisplayNodeSubclassOverridesSelector(c, @selector(layoutSpecThatFits:))) {
overrides |= ASDisplayNodeMethodOverrideLayoutSpecThatFits;
}
if (ASDisplayNodeSubclassOverridesSelector(c, @selector(calculateLayoutThatFits:)) ||
ASDisplayNodeSubclassOverridesSelector(c, @selector(calculateLayoutThatFits:
restrictedToSize:
relativeToParentSize:))) {
overrides |= ASDisplayNodeMethodOverrideCalcLayoutThatFits;
}
if (ASDisplayNodeSubclassOverridesSelector(c, @selector(calculateSizeThatFits:))) {
overrides |= ASDisplayNodeMethodOverrideCalcSizeThatFits;
}
if (ASDisplayNodeSubclassOverridesSelector(c, @selector(fetchData))) {
overrides |= ASDisplayNodeMethodOverrideFetchData;
}
Expand Down
1 change: 1 addition & 0 deletions Source/Layout/ASLayoutElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ typedef NS_ENUM(NSUInteger, ASLayoutElementType) {
restrictedToSize:(ASLayoutElementSize)size
relativeToParentSize:(CGSize)parentSize;

- (BOOL)implementsLayoutMethod;

#pragma mark - Deprecated

Expand Down
5 changes: 5 additions & 0 deletions Source/Layout/ASLayoutSpec.mm
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ - (BOOL)canLayoutAsynchronous
return YES;
}

- (BOOL)implementsLayoutMethod
{
return YES;
}

#pragma mark - Style

- (ASLayoutElementStyle *)style
Expand Down
5 changes: 3 additions & 2 deletions Source/Layout/ASYogaUtilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
@interface ASDisplayNode (YogaHelpers)

+ (ASDisplayNode *)yogaNode;
+ (ASDisplayNode *)verticalYogaStack;
+ (ASDisplayNode *)horizontalYogaStack;
+ (ASDisplayNode *)yogaSpacerNode;
+ (ASDisplayNode *)yogaVerticalStack;
+ (ASDisplayNode *)yogaHorizontalStack;

@end

Expand Down
43 changes: 27 additions & 16 deletions Source/Layout/ASYogaUtilities.mm
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,25 @@ + (ASDisplayNode *)yogaNode
return node;
}

+ (ASDisplayNode *)verticalYogaStack
+ (ASDisplayNode *)yogaSpacerNode
{
ASDisplayNode *stack = [self yogaNode];
stack.style.flexDirection = ASStackLayoutDirectionVertical;
return stack;
ASDisplayNode *node = [ASDisplayNode yogaNode];
node.style.flexGrow = 1.0f;
return node;
}

+ (ASDisplayNode *)horizontalYogaStack
+ (ASDisplayNode *)yogaVerticalStack
{
ASDisplayNode *stack = [self yogaNode];
stack.style.flexDirection = ASStackLayoutDirectionHorizontal;
return stack;
ASDisplayNode *node = [self yogaNode];
node.style.flexDirection = ASStackLayoutDirectionVertical;
return node;
}

+ (ASDisplayNode *)yogaHorizontalStack
{
ASDisplayNode *node = [self yogaNode];
node.style.flexDirection = ASStackLayoutDirectionHorizontal;
return node;
}

@end
Expand Down Expand Up @@ -141,14 +148,18 @@ void ASLayoutElementYogaUpdateMeasureFunc(YGNodeRef yogaNode, id <ASLayoutElemen
return;
}
BOOL hasMeasureFunc = (YGNodeGetMeasureFunc(yogaNode) != NULL);
if (layoutElement != nil && hasMeasureFunc == NO) {
// TODO(appleguy): Add override detection for calculateSizeThatFits: and calculateLayoutThatFits:,
// then we can set the MeasureFunc only for nodes that override one of the trio of measurement methods.
// if (_layoutSpecBlock == NULL && (_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) == 0 && ...) {
// Retain the Context object. This must be explicitly released with a __bridge_transfer; YGNodeFree() is not sufficient.
YGNodeSetContext(yogaNode, (__bridge_retained void *)layoutElement);
YGNodeSetMeasureFunc(yogaNode, &ASLayoutElementYogaMeasureFunc);
} else if (layoutElement == nil && hasMeasureFunc == YES){

if (layoutElement != nil && [layoutElement implementsLayoutMethod]) {
if (hasMeasureFunc == NO) {
// Retain the Context object. This must be explicitly released with a
// __bridge_transfer - YGNodeFree() is not sufficient.
YGNodeSetContext(yogaNode, (__bridge_retained void *)layoutElement);
YGNodeSetMeasureFunc(yogaNode, &ASLayoutElementYogaMeasureFunc);
}
ASDisplayNodeCAssert(YGNodeGetContext(yogaNode) == (__bridge void *)layoutElement,
@"Yoga node context should contain layoutElement: %@", layoutElement);
} else if (hasMeasureFunc == YES) {
// If we lack any of the conditions above, and currently have a measure func, get rid of it.
// Release the __bridge_retained Context object.
__unused id <ASLayoutElement> element = (__bridge_transfer id)YGNodeGetContext(yogaNode);
YGNodeSetContext(yogaNode, NULL);
Expand Down
6 changes: 4 additions & 2 deletions Source/Private/ASDisplayNodeInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,10 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides)
ASDisplayNodeMethodOverrideTouchesEnded = 1 << 2,
ASDisplayNodeMethodOverrideTouchesMoved = 1 << 3,
ASDisplayNodeMethodOverrideLayoutSpecThatFits = 1 << 4,
ASDisplayNodeMethodOverrideFetchData = 1 << 5,
ASDisplayNodeMethodOverrideClearFetchedData = 1 << 6
ASDisplayNodeMethodOverrideCalcLayoutThatFits = 1 << 5,
ASDisplayNodeMethodOverrideCalcSizeThatFits = 1 << 6,
ASDisplayNodeMethodOverrideFetchData = 1 << 7,
ASDisplayNodeMethodOverrideClearFetchedData = 1 << 8
};

typedef NS_OPTIONS(uint_least32_t, ASDisplayNodeAtomicFlags)
Expand Down
13 changes: 4 additions & 9 deletions examples/ASDKgram/Sample/PhotoCellNode.m
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ - (void)setupYogaLayoutIfNeeded
[_photoLocationLabel.style yogaNodeCreateIfNeeded];
[_photoTimeIntervalSincePostLabel.style yogaNodeCreateIfNeeded];

ASDisplayNode *headerStack = [ASDisplayNode horizontalYogaStack];
ASDisplayNode *headerStack = [ASDisplayNode yogaHorizontalStack];
headerStack.style.margin = ASEdgeInsetsMake(InsetForHeader);
headerStack.style.alignItems = ASStackLayoutAlignItemsCenter;
headerStack.style.flexGrow = 1.0;
Expand All @@ -327,7 +327,7 @@ - (void)setupYogaLayoutIfNeeded
[headerStack addYogaChild:_userAvatarImageNode];

// User Name and Photo Location stack is next
ASDisplayNode *userPhotoLocationStack = [ASDisplayNode verticalYogaStack];
ASDisplayNode *userPhotoLocationStack = [ASDisplayNode yogaVerticalStack];
userPhotoLocationStack.style.flexShrink = 1.0;
[headerStack addYogaChild:userPhotoLocationStack];

Expand All @@ -340,20 +340,15 @@ - (void)setupYogaLayoutIfNeeded
[userPhotoLocationStack addYogaChild:_photoLocationLabel];
}

/* TODO: These parameters aren't working as expected. For now the timestamp is next to the username.
// Add a spacer to allow a flexible space between the User Name / Location stack, and the Timestamp.
ASDisplayNode *spacer = [ASDisplayNode new];
spacer.style.flexShrink = 1.0;
spacer.style.width = ASDimensionMakeWithFraction(1.0);
[headerStack addYogaChild:spacer];
*/
[headerStack addYogaChild:[ASDisplayNode yogaSpacerNode]];

// Photo Timestamp Label.
_photoTimeIntervalSincePostLabel.style.spacingBefore = HORIZONTAL_BUFFER;
[headerStack addYogaChild:_photoTimeIntervalSincePostLabel];

// Create the last stack before assembling everything: the Footer Stack contains the description and comments.
ASDisplayNode *footerStack = [ASDisplayNode verticalYogaStack];
ASDisplayNode *footerStack = [ASDisplayNode yogaVerticalStack];
footerStack.style.margin = ASEdgeInsetsMake(InsetForFooter);
footerStack.style.padding = ASEdgeInsetsMake(UIEdgeInsetsMake(0.0, 0.0, VERTICAL_BUFFER, 0.0));
footerStack.yogaChildren = @[_photoLikesLabel, _photoDescriptionLabel, _photoCommentsNode];
Expand Down