-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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] Rewrite YOGA_TREE_CONTIGUOUS mode with improved behavior and cleaner integration #343
Changes from 1 commit
8027834
3467dce
70d81ae
c2e285d
818b4b2
18ea1c2
3546426
91a9c77
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -270,7 +270,6 @@ - (void)_initializeInstance | |
#if ASEVENTLOG_ENABLE | ||
_eventLog = [[ASEventLog alloc] initWithObject:self]; | ||
#endif | ||
|
||
_viewClass = [self.class viewClass]; | ||
_layerClass = [self.class layerClass]; | ||
_contentsScaleForDisplay = ASScreenScale(); | ||
|
@@ -424,12 +423,6 @@ - (void)dealloc | |
[self _scheduleIvarsForMainDeallocation]; | ||
} | ||
|
||
#if YOGA_TREE_CONTIGUOUS | ||
if (_yogaNode != NULL) { | ||
YGNodeFree(_yogaNode); | ||
} | ||
#endif | ||
|
||
// TODO: Remove this? If supernode isn't already nil, this method isn't dealloc-safe anyway. | ||
[self _setSupernode:nil]; | ||
} | ||
|
@@ -966,22 +959,26 @@ - (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize | |
ASDN::MutexLocker l(__instanceLock__); | ||
|
||
#if YOGA_TREE_CONTIGUOUS /* YOGA */ | ||
if (ASHierarchyStateIncludesYogaLayoutEnabled(_hierarchyState) == YES) { | ||
if (ASHierarchyStateIncludesYogaLayoutMeasuring(_hierarchyState) == NO && self.yogaCalculatedLayout == nil) { | ||
ASDN::MutexUnlocker ul(__instanceLock__); | ||
// There are several cases where Yoga could arrive here: | ||
// - This node is not in a Yoga tree: it has neither a yogaParent nor yogaChildren. | ||
// - This node is a Yoga tree root: it has no yogaParent, but has yogaChildren. | ||
// - This node is a Yoga tree node: it has both a yogaParent and yogaChildren. | ||
// - This node is a Yoga tree leaf: it has a yogaParent, but no yogaChidlren. | ||
// If we're a leaf node, we are probably being called by a measure function and proceed as normal. | ||
// If we're a yoga root or tree node, initiate a new Yoga calculation pass from root. | ||
YGNodeRef yogaNode = _style.yogaNode; | ||
if (yogaNode && (_yogaParent == nil || _yogaChildren.count > 0)) { | ||
// This node has some connection to a Yoga tree. | ||
ASDN::MutexUnlocker ul(__instanceLock__); | ||
if (self.yogaLayoutInProgress == NO) { | ||
[self calculateLayoutFromYogaRoot:constrainedSize]; | ||
} | ||
|
||
// The call above may set yogaCalculatedLayout, even if it tested as nil to enter it. | ||
if (self.yogaCalculatedLayout && self.yogaChildren.count > 0) { | ||
return self.yogaCalculatedLayout; | ||
} | ||
return _yogaCalculatedLayout; | ||
} | ||
#endif /* YOGA */ | ||
|
||
// Manual size calculation via calculateSizeThatFits: | ||
if (((_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) || | ||
(_layoutSpecBlock != NULL)) == NO) { | ||
if (_layoutSpecBlock == NULL && (_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) == 0) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This condition is equivalent to the previous one, but easier to read the intention of. |
||
CGSize size = [self calculateSizeThatFits:constrainedSize.max]; | ||
ASDisplayNodeLogEvent(self, @"calculatedSize: %@", NSStringFromCGSize(size)); | ||
return [ASLayout layoutWithLayoutElement:self size:ASSizeRangeClamp(constrainedSize, size) sublayouts:nil]; | ||
|
@@ -2894,7 +2891,9 @@ - (void)didEnterPreloadState | |
|
||
// Trigger a layout pass to ensure all subnodes have the correct size to preload their content. | ||
// This is important for image nodes, as well as collection and table nodes. | ||
[self layoutIfNeeded]; | ||
if (CGRectEqualToRect(self.bounds, CGRectZero) == NO) { | ||
[self layoutIfNeeded]; | ||
} | ||
|
||
if (_methodOverrides & ASDisplayNodeMethodOverrideFetchData) { | ||
#pragma clang diagnostic push | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -391,7 +391,7 @@ - (void)_insertElementsIntoMap:(ASMutableElementMap *)map | |
nodeBlock = [dataSource dataController:self supplementaryNodeBlockOfKind:kind atIndexPath:indexPath]; | ||
} | ||
|
||
ASSizeRange constrainedSize; | ||
ASSizeRange constrainedSize = ASSizeRangeUnconstrained; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This fixes an Xcode 9 static analyzer warning. I think it's the right default - if it is ever encountered - but please take a moment to consider it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @nguyenhuy would know better, but I believe this is fine because if we don't fetch size ranges at this stage, it means that they're using async layout and they'll perform measurement (and provide a size range) on their own if they want to. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, that's a sensible default value. If |
||
if (shouldFetchSizeRanges) { | ||
constrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPath]; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it is appropriate to use ASWeakProxy here, and strong-retain the proxy itself? Otherwise it seems a retain cycle would be created.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, ASWeakProxy seems to me like the best way to go here. I believe we could only avoid it if we could guarantee that Yoga will never perform any measurement after a node begins deallocation, probably not possible.