react-native/React/Views/RCTShadowView.m

692 lines
24 KiB
Mathematica
Raw Normal View History

/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
2015-01-30 01:10:49 +00:00
#import "RCTShadowView.h"
#import "RCTConvert.h"
#import "RCTI18nUtil.h"
#import "RCTLayout.h"
2015-01-30 01:10:49 +00:00
#import "RCTLog.h"
#import "RCTShadowView+Layout.h"
2015-01-30 01:10:49 +00:00
#import "RCTUtils.h"
Implement CSS z-index for iOS Summary: This diff implement the CSS z-index for React Native iOS views. We've had numerous pull request for this feature, but they've all attempted to use the `layer.zPosition` property, which is problematic for two reasons: 1. zPosition only affects rendering order, not event processing order. Views with a higher zPosition will appear in front of others in the hierarchy, but won't be the first to receive touch events, and may be blocked by views that are visually behind them. 2. when using a perspective transform matrix, views with a nonzero zPosition will be rendered in a different position due to parallax, which probably isn't desirable. See https://github.com/facebook/react-native/pull/7825 for further discussion of this problem. So instead of using `layer.zPosition`, I've implemented this by actually adjusting the order of the subviews within their parent based on the zIndex. This can't be done on the JS side because it would affect layout, which is order-dependent, so I'm doing it inside the view itself. It works as follows: 1. The `reactSubviews` array is set, whose order matches the order of the JS components and shadowView components, as specified by the UIManager. 2. `didUpdateReactSubviews` is called, which in turn calls `sortedSubviews` (which lazily generates a sorted array of `reactSubviews` by zIndex) and inserts the result into the view. 3. If a subview is added or removed, or the zIndex of any subview is changed, the previous `sortedSubviews` array is cleared and `didUpdateReactSubviews` is called again. To demonstrate it working, I've modified the UIExplorer example from https://github.com/facebook/react-native/pull/7825 Reviewed By: javache Differential Revision: D3365717 fbshipit-source-id: b34aa8bfad577bce023f8af5414f9b974aafd8aa
2016-06-07 14:40:25 +00:00
#import "UIView+Private.h"
#import "UIView+React.h"
2015-01-30 01:10:49 +00:00
typedef void (^RCTActionBlock)(RCTShadowView *shadowViewSelf, id value);
typedef void (^RCTResetActionBlock)(RCTShadowView *shadowViewSelf);
typedef NS_ENUM(unsigned int, meta_prop_t) {
2015-01-30 01:10:49 +00:00
META_PROP_LEFT,
META_PROP_TOP,
META_PROP_RIGHT,
META_PROP_BOTTOM,
META_PROP_START,
META_PROP_END,
2015-01-30 01:10:49 +00:00
META_PROP_HORIZONTAL,
META_PROP_VERTICAL,
META_PROP_ALL,
META_PROP_COUNT,
};
2015-01-30 01:10:49 +00:00
@implementation RCTShadowView
{
NSDictionary *_lastParentProperties;
NSMutableArray<RCTShadowView *> *_reactSubviews;
2015-01-30 01:10:49 +00:00
BOOL _recomputePadding;
BOOL _recomputeMargin;
2015-07-30 13:20:28 +00:00
BOOL _recomputeBorder;
YGValue _paddingMetaProps[META_PROP_COUNT];
YGValue _marginMetaProps[META_PROP_COUNT];
YGValue _borderMetaProps[META_PROP_COUNT];
2015-01-30 01:10:49 +00:00
}
+ (YGConfigRef)yogaConfig
{
static YGConfigRef yogaConfig;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
yogaConfig = YGConfigNew();
YGConfigSetPointScaleFactor(yogaConfig, RCTScreenScale());
YGConfigSetUseLegacyStretchBehaviour(yogaConfig, true);
});
return yogaConfig;
}
2015-01-30 01:10:49 +00:00
@synthesize reactTag = _reactTag;
// YogaNode API
2015-01-30 01:10:49 +00:00
static void RCTPrint(YGNodeRef node)
2015-01-30 01:10:49 +00:00
{
RCTShadowView *shadowView = (__bridge RCTShadowView *)YGNodeGetContext(node);
printf("%s(%lld), ", shadowView.viewName.UTF8String, (long long)shadowView.reactTag.integerValue);
2015-01-30 01:10:49 +00:00
}
#define RCT_SET_YGVALUE(ygvalue, setter, ...) \
switch (ygvalue.unit) { \
case YGUnitAuto: \
case YGUnitUndefined: \
setter(__VA_ARGS__, YGUndefined); \
break; \
case YGUnitPoint: \
setter(__VA_ARGS__, ygvalue.value); \
break; \
case YGUnitPercent: \
setter##Percent(__VA_ARGS__, ygvalue.value); \
break; \
}
#define RCT_SET_YGVALUE_AUTO(ygvalue, setter, ...) \
switch (ygvalue.unit) { \
case YGUnitAuto: \
setter##Auto(__VA_ARGS__); \
break; \
case YGUnitUndefined: \
setter(__VA_ARGS__, YGUndefined); \
break; \
case YGUnitPoint: \
setter(__VA_ARGS__, ygvalue.value); \
break; \
case YGUnitPercent: \
setter##Percent(__VA_ARGS__, ygvalue.value); \
break; \
}
static void RCTProcessMetaPropsPadding(const YGValue metaProps[META_PROP_COUNT], YGNodeRef node) {
if (![[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL]) {
RCT_SET_YGVALUE(metaProps[META_PROP_START], YGNodeStyleSetPadding, node, YGEdgeStart);
RCT_SET_YGVALUE(metaProps[META_PROP_END], YGNodeStyleSetPadding, node, YGEdgeEnd);
RCT_SET_YGVALUE(metaProps[META_PROP_LEFT], YGNodeStyleSetPadding, node, YGEdgeLeft);
RCT_SET_YGVALUE(metaProps[META_PROP_RIGHT], YGNodeStyleSetPadding, node, YGEdgeRight);
} else {
YGValue start = metaProps[META_PROP_START].unit == YGUnitUndefined ? metaProps[META_PROP_LEFT] : metaProps[META_PROP_START];
YGValue end = metaProps[META_PROP_END].unit == YGUnitUndefined ? metaProps[META_PROP_RIGHT] : metaProps[META_PROP_END];
RCT_SET_YGVALUE(start, YGNodeStyleSetPadding, node, YGEdgeStart);
RCT_SET_YGVALUE(end, YGNodeStyleSetPadding, node, YGEdgeEnd);
}
RCT_SET_YGVALUE(metaProps[META_PROP_TOP], YGNodeStyleSetPadding, node, YGEdgeTop);
RCT_SET_YGVALUE(metaProps[META_PROP_BOTTOM], YGNodeStyleSetPadding, node, YGEdgeBottom);
RCT_SET_YGVALUE(metaProps[META_PROP_HORIZONTAL], YGNodeStyleSetPadding, node, YGEdgeHorizontal);
RCT_SET_YGVALUE(metaProps[META_PROP_VERTICAL], YGNodeStyleSetPadding, node, YGEdgeVertical);
RCT_SET_YGVALUE(metaProps[META_PROP_ALL], YGNodeStyleSetPadding, node, YGEdgeAll);
}
static void RCTProcessMetaPropsMargin(const YGValue metaProps[META_PROP_COUNT], YGNodeRef node) {
if (![[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL]) {
RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_START], YGNodeStyleSetMargin, node, YGEdgeStart);
RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_END], YGNodeStyleSetMargin, node, YGEdgeEnd);
RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_LEFT], YGNodeStyleSetMargin, node, YGEdgeLeft);
RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_RIGHT], YGNodeStyleSetMargin, node, YGEdgeRight);
} else {
YGValue start = metaProps[META_PROP_START].unit == YGUnitUndefined ? metaProps[META_PROP_LEFT] : metaProps[META_PROP_START];
YGValue end = metaProps[META_PROP_END].unit == YGUnitUndefined ? metaProps[META_PROP_RIGHT] : metaProps[META_PROP_END];
RCT_SET_YGVALUE_AUTO(start, YGNodeStyleSetMargin, node, YGEdgeStart);
RCT_SET_YGVALUE_AUTO(end, YGNodeStyleSetMargin, node, YGEdgeEnd);
}
RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_TOP], YGNodeStyleSetMargin, node, YGEdgeTop);
RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_BOTTOM], YGNodeStyleSetMargin, node, YGEdgeBottom);
RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_HORIZONTAL], YGNodeStyleSetMargin, node, YGEdgeHorizontal);
RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_VERTICAL], YGNodeStyleSetMargin, node, YGEdgeVertical);
RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_ALL], YGNodeStyleSetMargin, node, YGEdgeAll);
}
static void RCTProcessMetaPropsBorder(const YGValue metaProps[META_PROP_COUNT], YGNodeRef node) {
if (![[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL]) {
YGNodeStyleSetBorder(node, YGEdgeStart, metaProps[META_PROP_START].value);
YGNodeStyleSetBorder(node, YGEdgeEnd, metaProps[META_PROP_END].value);
YGNodeStyleSetBorder(node, YGEdgeLeft, metaProps[META_PROP_LEFT].value);
YGNodeStyleSetBorder(node, YGEdgeRight, metaProps[META_PROP_RIGHT].value);
} else {
const float start = YGFloatIsUndefined(metaProps[META_PROP_START].value) ? metaProps[META_PROP_LEFT].value : metaProps[META_PROP_START].value;
const float end = YGFloatIsUndefined(metaProps[META_PROP_END].value) ? metaProps[META_PROP_RIGHT].value : metaProps[META_PROP_END].value;
YGNodeStyleSetBorder(node, YGEdgeStart, start);
YGNodeStyleSetBorder(node, YGEdgeEnd, end);
}
YGNodeStyleSetBorder(node, YGEdgeTop, metaProps[META_PROP_TOP].value);
YGNodeStyleSetBorder(node, YGEdgeBottom, metaProps[META_PROP_BOTTOM].value);
YGNodeStyleSetBorder(node, YGEdgeHorizontal, metaProps[META_PROP_HORIZONTAL].value);
YGNodeStyleSetBorder(node, YGEdgeVertical, metaProps[META_PROP_VERTICAL].value);
YGNodeStyleSetBorder(node, YGEdgeAll, metaProps[META_PROP_ALL].value);
}
2015-01-30 01:10:49 +00:00
2015-03-25 02:34:12 +00:00
- (CGRect)measureLayoutRelativeToAncestor:(RCTShadowView *)ancestor
2015-01-30 01:10:49 +00:00
{
2015-06-01 15:34:09 +00:00
CGPoint offset = CGPointZero;
2015-03-25 02:34:12 +00:00
NSInteger depth = 30; // max depth to search
RCTShadowView *shadowView = self;
while (depth && shadowView && shadowView != ancestor) {
offset.x += shadowView.layoutMetrics.frame.origin.x;
offset.y += shadowView.layoutMetrics.frame.origin.y;
2015-01-30 01:10:49 +00:00
shadowView = shadowView->_superview;
2015-03-25 02:34:12 +00:00
depth--;
2015-01-30 01:10:49 +00:00
}
if (ancestor != shadowView) {
return CGRectNull;
}
return (CGRect){offset, self.layoutMetrics.frame.size};
2015-01-30 01:10:49 +00:00
}
- (BOOL)viewIsDescendantOf:(RCTShadowView *)ancestor
{
NSInteger depth = 30; // max depth to search
RCTShadowView *shadowView = self;
while (depth && shadowView && shadowView != ancestor) {
shadowView = shadowView->_superview;
depth--;
}
return ancestor == shadowView;
}
2015-01-30 01:10:49 +00:00
- (instancetype)init
{
if (self = [super init]) {
for (unsigned int ii = 0; ii < META_PROP_COUNT; ii++) {
_paddingMetaProps[ii] = YGValueUndefined;
_marginMetaProps[ii] = YGValueUndefined;
_borderMetaProps[ii] = YGValueUndefined;
2015-01-30 01:10:49 +00:00
}
_intrinsicContentSize = CGSizeMake(UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric);
2015-01-30 01:10:49 +00:00
_newView = YES;
_reactSubviews = [NSMutableArray array];
_yogaNode = YGNodeNewWithConfig([[self class] yogaConfig]);
YGNodeSetContext(_yogaNode, (__bridge void *)self);
YGNodeSetPrintFunc(_yogaNode, RCTPrint);
2015-01-30 01:10:49 +00:00
}
return self;
}
2015-03-25 02:34:12 +00:00
- (BOOL)isReactRootView
{
return RCTIsReactRootView(self.reactTag);
}
2015-01-30 01:10:49 +00:00
- (void)dealloc
{
YGNodeFree(_yogaNode);
2015-01-30 01:10:49 +00:00
}
- (BOOL)canHaveSubviews
{
return YES;
}
- (BOOL)isYogaLeafNode
{
return NO;
}
2015-01-30 01:10:49 +00:00
- (void)insertReactSubview:(RCTShadowView *)subview atIndex:(NSInteger)atIndex
{
RCTAssert(self.canHaveSubviews, @"Attempt to insert subview inside leaf view.");
2015-01-30 01:10:49 +00:00
[_reactSubviews insertObject:subview atIndex:atIndex];
if (![self isYogaLeafNode]) {
YGNodeInsertChild(_yogaNode, subview.yogaNode, (uint32_t)atIndex);
}
2015-01-30 01:10:49 +00:00
subview->_superview = self;
}
- (void)removeReactSubview:(RCTShadowView *)subview
{
subview->_superview = nil;
[_reactSubviews removeObject:subview];
if (![self isYogaLeafNode]) {
YGNodeRemoveChild(_yogaNode, subview.yogaNode);
}
2015-01-30 01:10:49 +00:00
}
- (NSArray<RCTShadowView *> *)reactSubviews
2015-01-30 01:10:49 +00:00
{
return _reactSubviews;
}
- (RCTShadowView *)reactSuperview
{
return _superview;
}
#pragma mark - Layout
- (void)layoutWithMinimumSize:(CGSize)minimumSize
maximumSize:(CGSize)maximumSize
layoutDirection:(UIUserInterfaceLayoutDirection)layoutDirection
layoutContext:(RCTLayoutContext)layoutContext
{
YGNodeRef yogaNode = _yogaNode;
CGSize oldMinimumSize = (CGSize){
RCTCoreGraphicsFloatFromYogaValue(YGNodeStyleGetMinWidth(yogaNode), 0.0),
RCTCoreGraphicsFloatFromYogaValue(YGNodeStyleGetMinHeight(yogaNode), 0.0)
};
if (!CGSizeEqualToSize(oldMinimumSize, minimumSize)) {
YGNodeStyleSetMinWidth(yogaNode, RCTYogaFloatFromCoreGraphicsFloat(minimumSize.width));
YGNodeStyleSetMinHeight(yogaNode, RCTYogaFloatFromCoreGraphicsFloat(minimumSize.height));
}
YGNodeCalculateLayout(
yogaNode,
RCTYogaFloatFromCoreGraphicsFloat(maximumSize.width),
RCTYogaFloatFromCoreGraphicsFloat(maximumSize.height),
RCTYogaLayoutDirectionFromUIKitLayoutDirection(layoutDirection)
);
RCTAssert(!YGNodeIsDirty(yogaNode), @"Attempt to get layout metrics from dirtied Yoga node.");
if (!YGNodeGetHasNewLayout(yogaNode)) {
return;
}
YGNodeSetHasNewLayout(yogaNode, false);
RCTLayoutMetrics layoutMetrics = RCTLayoutMetricsFromYogaNode(yogaNode);
layoutContext.absolutePosition.x += layoutMetrics.frame.origin.x;
layoutContext.absolutePosition.y += layoutMetrics.frame.origin.y;
[self layoutWithMetrics:layoutMetrics
layoutContext:layoutContext];
[self layoutSubviewsWithContext:layoutContext];
}
- (void)layoutWithMetrics:(RCTLayoutMetrics)layoutMetrics
layoutContext:(RCTLayoutContext)layoutContext
{
if (!RCTLayoutMetricsEqualToLayoutMetrics(self.layoutMetrics, layoutMetrics)) {
self.layoutMetrics = layoutMetrics;
[layoutContext.affectedShadowViews addObject:self];
}
}
- (void)layoutSubviewsWithContext:(RCTLayoutContext)layoutContext
{
RCTLayoutMetrics layoutMetrics = self.layoutMetrics;
if (layoutMetrics.displayType == RCTDisplayTypeNone) {
return;
}
for (RCTShadowView *childShadowView in _reactSubviews) {
YGNodeRef childYogaNode = childShadowView.yogaNode;
RCTAssert(!YGNodeIsDirty(childYogaNode), @"Attempt to get layout metrics from dirtied Yoga node.");
if (!YGNodeGetHasNewLayout(childYogaNode)) {
continue;
}
YGNodeSetHasNewLayout(childYogaNode, false);
RCTLayoutMetrics childLayoutMetrics = RCTLayoutMetricsFromYogaNode(childYogaNode);
layoutContext.absolutePosition.x += childLayoutMetrics.frame.origin.x;
layoutContext.absolutePosition.y += childLayoutMetrics.frame.origin.y;
[childShadowView layoutWithMetrics:childLayoutMetrics
layoutContext:layoutContext];
// Recursive call.
[childShadowView layoutSubviewsWithContext:layoutContext];
}
}
- (CGSize)sizeThatFitsMinimumSize:(CGSize)minimumSize maximumSize:(CGSize)maximumSize
{
YGNodeRef clonedYogaNode = YGNodeClone(self.yogaNode);
YGNodeRef constraintYogaNode = YGNodeNewWithConfig([[self class] yogaConfig]);
YGNodeInsertChild(constraintYogaNode, clonedYogaNode, 0);
YGNodeStyleSetMinWidth(constraintYogaNode, RCTYogaFloatFromCoreGraphicsFloat(minimumSize.width));
YGNodeStyleSetMinHeight(constraintYogaNode, RCTYogaFloatFromCoreGraphicsFloat(minimumSize.height));
YGNodeStyleSetMaxWidth(constraintYogaNode, RCTYogaFloatFromCoreGraphicsFloat(maximumSize.width));
YGNodeStyleSetMaxHeight(constraintYogaNode, RCTYogaFloatFromCoreGraphicsFloat(maximumSize.height));
YGNodeCalculateLayout(
constraintYogaNode,
YGUndefined,
YGUndefined,
RCTYogaLayoutDirectionFromUIKitLayoutDirection(self.layoutMetrics.layoutDirection)
);
CGSize measuredSize = (CGSize){
RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetWidth(constraintYogaNode)),
RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetHeight(constraintYogaNode)),
};
YGNodeRemoveChild(constraintYogaNode, clonedYogaNode);
YGNodeFree(constraintYogaNode);
YGNodeFree(clonedYogaNode);
return measuredSize;
}
- (NSNumber *)reactTagAtPoint:(CGPoint)point
{
for (RCTShadowView *shadowView in _reactSubviews) {
if (CGRectContainsPoint(shadowView.layoutMetrics.frame, point)) {
CGPoint relativePoint = point;
CGPoint origin = shadowView.layoutMetrics.frame.origin;
relativePoint.x -= origin.x;
relativePoint.y -= origin.y;
return [shadowView reactTagAtPoint:relativePoint];
}
}
return self.reactTag;
}
- (NSString *)description
{
NSString *description = super.description;
description = [[description substringToIndex:description.length - 1] stringByAppendingFormat:@"; viewName: %@; reactTag: %@; frame: %@>", self.viewName, self.reactTag, NSStringFromCGRect(self.layoutMetrics.frame)];
return description;
}
- (void)addRecursiveDescriptionToString:(NSMutableString *)string atLevel:(NSUInteger)level
{
for (NSUInteger i = 0; i < level; i++) {
[string appendString:@" | "];
}
[string appendString:self.description];
[string appendString:@"\n"];
for (RCTShadowView *subview in _reactSubviews) {
[subview addRecursiveDescriptionToString:string atLevel:level + 1];
}
}
- (NSString *)recursiveDescription
{
NSMutableString *description = [NSMutableString string];
[self addRecursiveDescriptionToString:description atLevel:0];
return description;
}
2015-01-30 01:10:49 +00:00
// Margin
#define RCT_MARGIN_PROPERTY(prop, metaProp) \
- (void)setMargin##prop:(YGValue)value \
2015-01-30 01:10:49 +00:00
{ \
_marginMetaProps[META_PROP_##metaProp] = value; \
_recomputeMargin = YES; \
} \
- (YGValue)margin##prop \
2015-01-30 01:10:49 +00:00
{ \
return _marginMetaProps[META_PROP_##metaProp]; \
}
RCT_MARGIN_PROPERTY(, ALL)
RCT_MARGIN_PROPERTY(Vertical, VERTICAL)
RCT_MARGIN_PROPERTY(Horizontal, HORIZONTAL)
RCT_MARGIN_PROPERTY(Top, TOP)
RCT_MARGIN_PROPERTY(Left, LEFT)
RCT_MARGIN_PROPERTY(Bottom, BOTTOM)
RCT_MARGIN_PROPERTY(Right, RIGHT)
RCT_MARGIN_PROPERTY(Start, START)
RCT_MARGIN_PROPERTY(End, END)
2015-01-30 01:10:49 +00:00
// Padding
#define RCT_PADDING_PROPERTY(prop, metaProp) \
- (void)setPadding##prop:(YGValue)value \
2015-01-30 01:10:49 +00:00
{ \
_paddingMetaProps[META_PROP_##metaProp] = value; \
_recomputePadding = YES; \
} \
- (YGValue)padding##prop \
2015-01-30 01:10:49 +00:00
{ \
return _paddingMetaProps[META_PROP_##metaProp]; \
2015-01-30 01:10:49 +00:00
}
RCT_PADDING_PROPERTY(, ALL)
RCT_PADDING_PROPERTY(Vertical, VERTICAL)
RCT_PADDING_PROPERTY(Horizontal, HORIZONTAL)
RCT_PADDING_PROPERTY(Top, TOP)
RCT_PADDING_PROPERTY(Left, LEFT)
RCT_PADDING_PROPERTY(Bottom, BOTTOM)
RCT_PADDING_PROPERTY(Right, RIGHT)
RCT_PADDING_PROPERTY(Start, START)
RCT_PADDING_PROPERTY(End, END)
2015-01-30 01:10:49 +00:00
// Border
#define RCT_BORDER_PROPERTY(prop, metaProp) \
- (void)setBorder##prop##Width:(float)value \
{ \
_borderMetaProps[META_PROP_##metaProp].value = value; \
_recomputeBorder = YES; \
} \
- (float)border##prop##Width \
{ \
return _borderMetaProps[META_PROP_##metaProp].value; \
}
2015-07-30 13:20:28 +00:00
RCT_BORDER_PROPERTY(, ALL)
RCT_BORDER_PROPERTY(Top, TOP)
RCT_BORDER_PROPERTY(Left, LEFT)
RCT_BORDER_PROPERTY(Bottom, BOTTOM)
RCT_BORDER_PROPERTY(Right, RIGHT)
RCT_BORDER_PROPERTY(Start, START)
RCT_BORDER_PROPERTY(End, END)
2015-01-30 01:10:49 +00:00
// Dimensions
#define RCT_DIMENSION_PROPERTY(setProp, getProp, cssProp) \
- (void)set##setProp:(YGValue)value \
{ \
RCT_SET_YGVALUE_AUTO(value, YGNodeStyleSet##cssProp, _yogaNode); \
} \
- (YGValue)getProp \
{ \
return YGNodeStyleGet##cssProp(_yogaNode); \
}
#define RCT_MIN_MAX_DIMENSION_PROPERTY(setProp, getProp, cssProp) \
- (void)set##setProp:(YGValue)value \
{ \
RCT_SET_YGVALUE(value, YGNodeStyleSet##cssProp, _yogaNode); \
} \
- (YGValue)getProp \
{ \
return YGNodeStyleGet##cssProp(_yogaNode); \
}
RCT_DIMENSION_PROPERTY(Width, width, Width)
RCT_DIMENSION_PROPERTY(Height, height, Height)
RCT_MIN_MAX_DIMENSION_PROPERTY(MinWidth, minWidth, MinWidth)
RCT_MIN_MAX_DIMENSION_PROPERTY(MinHeight, minHeight, MinHeight)
RCT_MIN_MAX_DIMENSION_PROPERTY(MaxWidth, maxWidth, MaxWidth)
RCT_MIN_MAX_DIMENSION_PROPERTY(MaxHeight, maxHeight, MaxHeight)
2015-01-30 01:10:49 +00:00
// Position
#define RCT_POSITION_PROPERTY(setProp, getProp, edge) \
- (void)set##setProp:(YGValue)value \
{ \
RCT_SET_YGVALUE(value, YGNodeStyleSetPosition, _yogaNode, edge); \
} \
- (YGValue)getProp \
{ \
return YGNodeStyleGetPosition(_yogaNode, edge); \
}
RCT_POSITION_PROPERTY(Top, top, YGEdgeTop)
RCT_POSITION_PROPERTY(Bottom, bottom, YGEdgeBottom)
RCT_POSITION_PROPERTY(Start, start, YGEdgeStart)
RCT_POSITION_PROPERTY(End, end, YGEdgeEnd)
- (void)setLeft:(YGValue)value
{
YGEdge edge = [[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL] ? YGEdgeStart : YGEdgeLeft;
RCT_SET_YGVALUE(value, YGNodeStyleSetPosition, _yogaNode, edge);
}
- (YGValue)left
{
YGEdge edge = [[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL] ? YGEdgeStart : YGEdgeLeft;
return YGNodeStyleGetPosition(_yogaNode, edge);
}
- (void)setRight:(YGValue)value
{
YGEdge edge = [[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL] ? YGEdgeEnd : YGEdgeRight;
RCT_SET_YGVALUE(value, YGNodeStyleSetPosition, _yogaNode, edge);
}
- (YGValue)right
{
YGEdge edge = [[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL] ? YGEdgeEnd : YGEdgeRight;
return YGNodeStyleGetPosition(_yogaNode, edge);
}
2015-01-30 01:10:49 +00:00
// Size
- (CGSize)size
2015-01-30 01:10:49 +00:00
{
YGValue width = YGNodeStyleGetWidth(_yogaNode);
YGValue height = YGNodeStyleGetHeight(_yogaNode);
return CGSizeMake(
width.unit == YGUnitPoint ? width.value : NAN,
height.unit == YGUnitPoint ? height.value : NAN
);
}
- (void)setSize:(CGSize)size
{
YGNodeStyleSetWidth(_yogaNode, size.width);
YGNodeStyleSetHeight(_yogaNode, size.height);
2015-01-30 01:10:49 +00:00
}
// IntrinsicContentSize
static inline YGSize RCTShadowViewMeasure(YGNodeRef node, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode)
{
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)intrinsicContentSize
{
if (CGSizeEqualToSize(_intrinsicContentSize, intrinsicContentSize)) {
return;
}
_intrinsicContentSize = intrinsicContentSize;
if (CGSizeEqualToSize(_intrinsicContentSize, CGSizeMake(UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric))) {
YGNodeSetMeasureFunc(_yogaNode, NULL);
} else {
YGNodeSetMeasureFunc(_yogaNode, RCTShadowViewMeasure);
}
YGNodeMarkDirty(_yogaNode);
}
// Local Data
- (void)setLocalData:(__unused NSObject *)localData
{
// Do nothing by default.
}
2015-01-30 01:10:49 +00:00
// Flex
- (void)setFlexBasis:(YGValue)value
{
RCT_SET_YGVALUE_AUTO(value, YGNodeStyleSetFlexBasis, _yogaNode);
}
- (YGValue)flexBasis
{
return YGNodeStyleGetFlexBasis(_yogaNode);
}
2015-01-30 01:10:49 +00:00
#define RCT_STYLE_PROPERTY(setProp, getProp, cssProp, type) \
- (void)set##setProp:(type)value \
{ \
YGNodeStyleSet##cssProp(_yogaNode, value); \
2015-01-30 01:10:49 +00:00
} \
- (type)getProp \
{ \
return YGNodeStyleGet##cssProp(_yogaNode); \
2015-01-30 01:10:49 +00:00
}
RCT_STYLE_PROPERTY(Flex, flex, Flex, float)
RCT_STYLE_PROPERTY(FlexGrow, flexGrow, FlexGrow, float)
RCT_STYLE_PROPERTY(FlexShrink, flexShrink, FlexShrink, float)
RCT_STYLE_PROPERTY(FlexDirection, flexDirection, FlexDirection, YGFlexDirection)
RCT_STYLE_PROPERTY(JustifyContent, justifyContent, JustifyContent, YGJustify)
RCT_STYLE_PROPERTY(AlignSelf, alignSelf, AlignSelf, YGAlign)
RCT_STYLE_PROPERTY(AlignItems, alignItems, AlignItems, YGAlign)
RCT_STYLE_PROPERTY(AlignContent, alignContent, AlignContent, YGAlign)
RCT_STYLE_PROPERTY(Position, position, PositionType, YGPositionType)
RCT_STYLE_PROPERTY(FlexWrap, flexWrap, FlexWrap, YGWrap)
RCT_STYLE_PROPERTY(Overflow, overflow, Overflow, YGOverflow)
RCT_STYLE_PROPERTY(Display, display, Display, YGDisplay)
RCT_STYLE_PROPERTY(Direction, direction, Direction, YGDirection)
RCT_STYLE_PROPERTY(AspectRatio, aspectRatio, AspectRatio, float)
2015-01-30 01:10:49 +00:00
- (void)didUpdateReactSubviews
{
// Does nothing by default
}
- (void)didSetProps:(__unused NSArray<NSString *> *)changedProps
{
if (_recomputePadding) {
RCTProcessMetaPropsPadding(_paddingMetaProps, _yogaNode);
}
if (_recomputeMargin) {
RCTProcessMetaPropsMargin(_marginMetaProps, _yogaNode);
}
2015-07-30 13:20:28 +00:00
if (_recomputeBorder) {
RCTProcessMetaPropsBorder(_borderMetaProps, _yogaNode);
2015-07-30 13:20:28 +00:00
}
_recomputeMargin = NO;
_recomputePadding = NO;
2015-07-30 13:20:28 +00:00
_recomputeBorder = NO;
}
2015-01-30 01:10:49 +00:00
@end