Better (right) implementation of `intrinsicContentSize`

Reviewed By: emilsjolander

Differential Revision: D4486767

fbshipit-source-id: d37ea11f9f48425d4d99c29e8bfb6c8ed2353f04
This commit is contained in:
Valentin Shergin 2017-01-31 16:47:11 -08:00 committed by Facebook Github Bot
parent 31099aa233
commit d1990f8fc4
4 changed files with 71 additions and 35 deletions

View File

@ -28,11 +28,10 @@
{
[super setUp];
self.parentView = [self _shadowViewWithConfig:^(YGNodeRef node) {
YGNodeStyleSetFlexDirection(node, YGFlexDirectionColumn);
YGNodeStyleSetWidth(node, 440);
YGNodeStyleSetHeight(node, 440);
}];
self.parentView = [RCTRootShadowView new];
YGNodeStyleSetFlexDirection(self.parentView.cssNode, YGFlexDirectionColumn);
YGNodeStyleSetWidth(self.parentView.cssNode, 440);
YGNodeStyleSetHeight(self.parentView.cssNode, 440);
self.parentView.reactTag = @1; // must be valid rootView tag
}
@ -132,6 +131,7 @@
- (void)testAssignsSuggestedWidthDimension
{
[self _withShadowViewWithStyle:^(YGNodeRef node) {
YGNodeStyleSetPositionType(node, YGPositionTypeAbsolute);
YGNodeStyleSetPosition(node, YGEdgeLeft, 0);
YGNodeStyleSetPosition(node, YGEdgeTop, 0);
YGNodeStyleSetHeight(node, 10);
@ -143,6 +143,7 @@
- (void)testAssignsSuggestedHeightDimension
{
[self _withShadowViewWithStyle:^(YGNodeRef node) {
YGNodeStyleSetPositionType(node, YGPositionTypeAbsolute);
YGNodeStyleSetPosition(node, YGEdgeLeft, 0);
YGNodeStyleSetPosition(node, YGEdgeTop, 0);
YGNodeStyleSetWidth(node, 10);
@ -154,6 +155,7 @@
- (void)testDoesNotOverrideDimensionStyleWithSuggestedDimensions
{
[self _withShadowViewWithStyle:^(YGNodeRef node) {
YGNodeStyleSetPositionType(node, YGPositionTypeAbsolute);
YGNodeStyleSetPosition(node, YGEdgeLeft, 0);
YGNodeStyleSetPosition(node, YGEdgeTop, 0);
YGNodeStyleSetWidth(node, 10);
@ -189,11 +191,12 @@
NSStringFromCGRect(actualRect));
}
- (RCTRootShadowView *)_shadowViewWithConfig:(void(^)(YGNodeRef node))configBlock
- (RCTShadowView *)_shadowViewWithConfig:(void(^)(YGNodeRef node))configBlock
{
RCTRootShadowView *shadowView = [RCTRootShadowView new];
RCTShadowView *shadowView = [RCTShadowView new];
configBlock(shadowView.cssNode);
return shadowView;
}
@end

View File

@ -473,11 +473,12 @@ dispatch_queue_t RCTGetUIManagerQueue(void)
NSNumber *reactTag = view.reactTag;
dispatch_async(RCTGetUIManagerQueue(), ^{
RCTShadowView *shadowView = self->_shadowViewRegistry[reactTag];
RCTAssert(shadowView != nil, @"Could not locate root view with tag #%@", reactTag);
RCTAssert(shadowView != nil, @"Could not locate view with tag #%@", reactTag);
shadowView.intrinsicContentSize = size;
[self setNeedsLayout];
if (!CGSizeEqualToSize(shadowView.intrinsicContentSize, size)) {
shadowView.intrinsicContentSize = size;
[self setNeedsLayout];
}
});
}

View File

@ -81,15 +81,16 @@ typedef void (^RCTApplierBlock)(NSDictionary<NSNumber *, UIView *> *viewRegistry
@property (nonatomic, assign) CGRect frame;
/**
* Represents the natural size of the view, which is used when explicit size is not set or is ambiguous.
* Defaults to `{UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric}`.
*/
@property (nonatomic, assign) CGSize intrinsicContentSize;
- (void)setTopLeft:(CGPoint)topLeft;
- (void)setSize:(CGSize)size;
/**
* Set the natural size of the view, which is used when no explicit size is set.
* Use UIViewNoIntrinsicMetric to ignore a dimension.
*/
- (void)setIntrinsicContentSize:(CGSize)size;
/**
* Border. Defaults to { 0, 0, 0, 0 }.
*/

View File

@ -301,6 +301,8 @@ static void RCTProcessMetaPropsBorder(const YGValue metaProps[META_PROP_COUNT],
_borderMetaProps[ii] = YGValueUndefined;
}
_intrinsicContentSize = CGSizeMake(UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric);
_newView = YES;
_propagationLifecycle = RCTUpdateLifecycleUninitialized;
_textLifecycle = RCTUpdateLifecycleUninitialized;
@ -556,30 +558,59 @@ RCT_POSITION_PROPERTY(Left, left, YGEdgeStart)
}
}
static inline void RCTAssignSuggestedDimension(YGNodeRef cssNode, YGDimension dimension, CGFloat amount)
static inline YGSize RCTShadowViewMeasure(YGNodeRef node, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode)
{
if (amount != UIViewNoIntrinsicMetric) {
switch (dimension) {
case YGDimensionWidth:
if (YGNodeStyleGetWidth(cssNode).unit == YGUnitUndefined) {
YGNodeStyleSetWidth(cssNode, amount);
}
break;
case YGDimensionHeight:
if (YGNodeStyleGetHeight(cssNode).unit == YGUnitUndefined) {
YGNodeStyleSetHeight(cssNode, amount);
}
break;
}
RCTShadowView *shadowView = (__bridge RCTShadowView *)YGNodeGetContext(node);
CGSize intrinsicContentSize = shadowView->_intrinsicContentSize;
// Replace `UIViewNoIntrinsicMetric` (which equals `-1`) with zero.
intrinsicContentSize.width = MAX(0, intrinsicContentSize.width);
intrinsicContentSize.height = MAX(0, intrinsicContentSize.height);
YGSize result;
switch (widthMode) {
case YGMeasureModeUndefined:
result.width = intrinsicContentSize.width;
break;
case YGMeasureModeExactly:
result.width = width;
break;
case YGMeasureModeAtMost:
result.width = MIN(width, intrinsicContentSize.width);
break;
}
switch (heightMode) {
case YGMeasureModeUndefined:
result.height = intrinsicContentSize.height;
break;
case YGMeasureModeExactly:
result.height = height;
break;
case YGMeasureModeAtMost:
result.height = MIN(height, intrinsicContentSize.height);
break;
}
return result;
}
- (void)setIntrinsicContentSize:(CGSize)size
- (void)setIntrinsicContentSize:(CGSize)intrinsicContentSize
{
if (YGNodeStyleGetFlexGrow(_cssNode) == 0 && YGNodeStyleGetFlexShrink(_cssNode) == 0) {
RCTAssignSuggestedDimension(_cssNode, YGDimensionHeight, size.height);
RCTAssignSuggestedDimension(_cssNode, YGDimensionWidth, size.width);
if (CGSizeEqualToSize(_intrinsicContentSize, intrinsicContentSize)) {
return;
}
_intrinsicContentSize = intrinsicContentSize;
if (CGSizeEqualToSize(_intrinsicContentSize, CGSizeMake(UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric))) {
YGNodeSetMeasureFunc(_cssNode, NULL);
} else {
YGNodeSetMeasureFunc(_cssNode, RCTShadowViewMeasure);
}
YGNodeMarkDirty(_cssNode);
}
- (void)setTopLeft:(CGPoint)topLeft