react-native/React/Views/RCTShadowView.m
alvaromb e52cab5a7f Adds the ability to use UIManager to check if a node is an ancestor
Summary:
Sometimes is handy to check if a React node is a descendant of another node or not. For instance, I want to check if the focused `TextInput` is descendant of an specific `ScrollView`:

```js
const currentlyFocusedField = TextInput.State.currentlyFocusedField()
UIManager.viewIsAncestorOf(
  currentlyFocusedField,
  this.getInnerViewNode(),
  (isAncestor) => {
    if (isAncestor) {
      console.log('The focused field is a descendant of this ScrollView!')
    }
  }
)
```

This function uses the same strategy as the `measureLayout` method to check if one node is an ancestor of other node. As the `measureLayout` method, this is performed outside the main thread.

By now I've only implemented the iOS version and its tests, but if this function is going to be merged I'll implement the Android version too. I have objc experience but no Java or Android, so I prefer to validate this functionality before jumping into developing the Android part.
Closes https://github.com/facebook/react-native/pull/7876

Differential Revision: D3662045

Pulled By: javache

fbshipit-source-id: b9668e8ea94fd01db76651f16243926cf9c2566f
2016-08-03 04:13:43 -07:00

660 lines
24 KiB
Objective-C

/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTShadowView.h"
#import "RCTConvert.h"
#import "RCTLog.h"
#import "RCTUtils.h"
#import "UIView+React.h"
#import "UIView+Private.h"
typedef void (^RCTActionBlock)(RCTShadowView *shadowViewSelf, id value);
typedef void (^RCTResetActionBlock)(RCTShadowView *shadowViewSelf);
static NSString *const RCTBackgroundColorProp = @"backgroundColor";
typedef NS_ENUM(unsigned int, meta_prop_t) {
META_PROP_LEFT,
META_PROP_TOP,
META_PROP_RIGHT,
META_PROP_BOTTOM,
META_PROP_HORIZONTAL,
META_PROP_VERTICAL,
META_PROP_ALL,
META_PROP_COUNT,
};
@implementation RCTShadowView
{
RCTUpdateLifecycle _propagationLifecycle;
RCTUpdateLifecycle _textLifecycle;
NSDictionary *_lastParentProperties;
NSMutableArray<RCTShadowView *> *_reactSubviews;
BOOL _recomputePadding;
BOOL _recomputeMargin;
BOOL _recomputeBorder;
BOOL _didUpdateSubviews;
float _paddingMetaProps[META_PROP_COUNT];
float _marginMetaProps[META_PROP_COUNT];
float _borderMetaProps[META_PROP_COUNT];
}
@synthesize reactTag = _reactTag;
// cssNode api
static void RCTPrint(void *context)
{
RCTShadowView *shadowView = (__bridge RCTShadowView *)context;
printf("%s(%zd), ", shadowView.viewName.UTF8String, shadowView.reactTag.integerValue);
}
// Enforces precedence rules, e.g. marginLeft > marginHorizontal > margin.
#define DEFINE_PROCESS_META_PROPS(type) \
static void RCTProcessMetaProps##type(const float metaProps[META_PROP_COUNT], CSSNodeRef node) { \
if (!isUndefined(metaProps[META_PROP_LEFT])) { \
CSSNodeStyleSet##type##Start(node, metaProps[META_PROP_LEFT]); \
} else if (!isUndefined(metaProps[META_PROP_HORIZONTAL])) { \
CSSNodeStyleSet##type##Start(node, metaProps[META_PROP_HORIZONTAL]); \
} else if (!isUndefined(metaProps[META_PROP_ALL])) { \
CSSNodeStyleSet##type##Start(node, metaProps[META_PROP_ALL]); \
} else { \
CSSNodeStyleSet##type##Start(node, 0); \
} \
\
if (!isUndefined(metaProps[META_PROP_RIGHT])) { \
CSSNodeStyleSet##type##End(node, metaProps[META_PROP_RIGHT]); \
} else if (!isUndefined(metaProps[META_PROP_HORIZONTAL])) { \
CSSNodeStyleSet##type##End(node, metaProps[META_PROP_HORIZONTAL]); \
} else if (!isUndefined(metaProps[META_PROP_ALL])) { \
CSSNodeStyleSet##type##End(node, metaProps[META_PROP_ALL]); \
} else { \
CSSNodeStyleSet##type##End(node, 0); \
} \
\
if (!isUndefined(metaProps[META_PROP_TOP])) { \
CSSNodeStyleSet##type##Top(node, metaProps[META_PROP_TOP]); \
} else if (!isUndefined(metaProps[META_PROP_VERTICAL])) { \
CSSNodeStyleSet##type##Top(node, metaProps[META_PROP_VERTICAL]); \
} else if (!isUndefined(metaProps[META_PROP_ALL])) { \
CSSNodeStyleSet##type##Top(node, metaProps[META_PROP_ALL]); \
} else { \
CSSNodeStyleSet##type##Top(node, 0); \
} \
\
if (!isUndefined(metaProps[META_PROP_BOTTOM])) { \
CSSNodeStyleSet##type##Bottom(node, metaProps[META_PROP_BOTTOM]); \
} else if (!isUndefined(metaProps[META_PROP_VERTICAL])) { \
CSSNodeStyleSet##type##Bottom(node, metaProps[META_PROP_VERTICAL]); \
} else if (!isUndefined(metaProps[META_PROP_ALL])) { \
CSSNodeStyleSet##type##Bottom(node, metaProps[META_PROP_ALL]); \
} else { \
CSSNodeStyleSet##type##Bottom(node, 0); \
} \
}
DEFINE_PROCESS_META_PROPS(Padding);
DEFINE_PROCESS_META_PROPS(Margin);
DEFINE_PROCESS_META_PROPS(Border);
// The absolute stuff is so that we can take into account our absolute position when rounding in order to
// snap to the pixel grid. For example, say you have the following structure:
//
// +--------+---------+--------+
// | |+-------+| |
// | || || |
// | |+-------+| |
// +--------+---------+--------+
//
// Say the screen width is 320 pts so the three big views will get the following x bounds from our layout system:
// {0, 106.667}, {106.667, 213.333}, {213.333, 320}
//
// Assuming screen scale is 2, these numbers must be rounded to the nearest 0.5 to fit the pixel grid:
// {0, 106.5}, {106.5, 213.5}, {213.5, 320}
// You'll notice that the three widths are 106.5, 107, 106.5.
//
// This is great for the parent views but it gets trickier when we consider rounding for the subview.
//
// When we go to round the bounds for the subview in the middle, it's relative bounds are {0, 106.667}
// which gets rounded to {0, 106.5}. This will cause the subview to be one pixel smaller than it should be.
// this is why we need to pass in the absolute position in order to do the rounding relative to the screen's
// grid rather than the view's grid.
//
// After passing in the absolutePosition of {106.667, y}, we do the following calculations:
// absoluteLeft = round(absolutePosition.x + viewPosition.left) = round(106.667 + 0) = 106.5
// absoluteRight = round(absolutePosition.x + viewPosition.left + viewSize.left) + round(106.667 + 0 + 106.667) = 213.5
// width = 213.5 - 106.5 = 107
// You'll notice that this is the same width we calculated for the parent view because we've taken its position into account.
- (void)applyLayoutNode:(CSSNodeRef)node
viewsWithNewFrame:(NSMutableSet<RCTShadowView *> *)viewsWithNewFrame
absolutePosition:(CGPoint)absolutePosition
{
if (!CSSNodeGetHasNewLayout(node)) {
return;
}
CSSNodeSetHasNewLayout(node, false);
CGPoint absoluteTopLeft = {
absolutePosition.x + CSSNodeLayoutGetLeft(node),
absolutePosition.y + CSSNodeLayoutGetTop(node)
};
CGPoint absoluteBottomRight = {
absolutePosition.x + CSSNodeLayoutGetLeft(node) + CSSNodeLayoutGetWidth(node),
absolutePosition.y + CSSNodeLayoutGetTop(node) + CSSNodeLayoutGetHeight(node)
};
CGRect frame = {{
RCTRoundPixelValue(CSSNodeLayoutGetLeft(node)),
RCTRoundPixelValue(CSSNodeLayoutGetTop(node)),
}, {
RCTRoundPixelValue(absoluteBottomRight.x - absoluteTopLeft.x),
RCTRoundPixelValue(absoluteBottomRight.y - absoluteTopLeft.y)
}};
if (!CGRectEqualToRect(frame, _frame)) {
_frame = frame;
[viewsWithNewFrame addObject:self];
}
absolutePosition.x += CSSNodeLayoutGetLeft(node);
absolutePosition.y += CSSNodeLayoutGetTop(node);
[self applyLayoutToChildren:node viewsWithNewFrame:viewsWithNewFrame absolutePosition:absolutePosition];
}
- (void)applyLayoutToChildren:(CSSNodeRef)node
viewsWithNewFrame:(NSMutableSet<RCTShadowView *> *)viewsWithNewFrame
absolutePosition:(CGPoint)absolutePosition
{
for (unsigned int i = 0; i < CSSNodeChildCount(node); ++i) {
RCTShadowView *child = (RCTShadowView *)_reactSubviews[i];
[child applyLayoutNode:CSSNodeGetChild(node, i)
viewsWithNewFrame:viewsWithNewFrame
absolutePosition:absolutePosition];
}
}
- (NSDictionary<NSString *, id> *)processUpdatedProperties:(NSMutableSet<RCTApplierBlock> *)applierBlocks
parentProperties:(NSDictionary<NSString *, id> *)parentProperties
{
// TODO: we always refresh all propagated properties when propagation is
// dirtied, but really we should track which properties have changed and
// only update those.
if (_didUpdateSubviews) {
_didUpdateSubviews = NO;
[self didUpdateReactSubviews];
[applierBlocks addObject:^(NSDictionary<NSNumber *, UIView *> *viewRegistry) {
UIView *view = viewRegistry[self->_reactTag];
[view clearSortedSubviews];
[view didUpdateReactSubviews];
}];
}
if (!_backgroundColor) {
UIColor *parentBackgroundColor = parentProperties[RCTBackgroundColorProp];
if (parentBackgroundColor) {
[applierBlocks addObject:^(NSDictionary<NSNumber *, UIView *> *viewRegistry) {
UIView *view = viewRegistry[self->_reactTag];
[view reactSetInheritedBackgroundColor:parentBackgroundColor];
}];
}
} else {
// Update parent properties for children
NSMutableDictionary<NSString *, id> *properties = [NSMutableDictionary dictionaryWithDictionary:parentProperties];
CGFloat alpha = CGColorGetAlpha(_backgroundColor.CGColor);
if (alpha < 1.0) {
// If bg is non-opaque, don't propagate further
properties[RCTBackgroundColorProp] = [UIColor clearColor];
} else {
properties[RCTBackgroundColorProp] = _backgroundColor;
}
return properties;
}
return parentProperties;
}
- (void)collectUpdatedProperties:(NSMutableSet<RCTApplierBlock> *)applierBlocks
parentProperties:(NSDictionary<NSString *, id> *)parentProperties
{
if (_propagationLifecycle == RCTUpdateLifecycleComputed && [parentProperties isEqualToDictionary:_lastParentProperties]) {
return;
}
_propagationLifecycle = RCTUpdateLifecycleComputed;
_lastParentProperties = parentProperties;
NSDictionary<NSString *, id> *nextProps = [self processUpdatedProperties:applierBlocks parentProperties:parentProperties];
for (RCTShadowView *child in _reactSubviews) {
[child collectUpdatedProperties:applierBlocks parentProperties:nextProps];
}
}
- (void)collectUpdatedFrames:(NSMutableSet<RCTShadowView *> *)viewsWithNewFrame
withFrame:(CGRect)frame
hidden:(BOOL)hidden
absolutePosition:(CGPoint)absolutePosition
{
if (_hidden != hidden) {
// The hidden state has changed. Even if the frame hasn't changed, add
// this ShadowView to viewsWithNewFrame so the UIManager will process
// this ShadowView's UIView and update its hidden state.
_hidden = hidden;
[viewsWithNewFrame addObject:self];
}
if (!CGRectEqualToRect(frame, _frame)) {
CSSNodeStyleSetPositionType(_cssNode, CSSPositionTypeAbsolute);
CSSNodeStyleSetWidth(_cssNode, frame.size.width);
CSSNodeStyleSetHeight(_cssNode, frame.size.height);
CSSNodeStyleSetPositionLeft(_cssNode, frame.origin.x);
CSSNodeStyleSetPositionTop(_cssNode, frame.origin.y);
}
CSSNodeCalculateLayout(_cssNode, frame.size.width, frame.size.height, CSSDirectionInherit);
[self applyLayoutNode:_cssNode viewsWithNewFrame:viewsWithNewFrame absolutePosition:absolutePosition];
}
- (CGRect)measureLayoutRelativeToAncestor:(RCTShadowView *)ancestor
{
CGPoint offset = CGPointZero;
NSInteger depth = 30; // max depth to search
RCTShadowView *shadowView = self;
while (depth && shadowView && shadowView != ancestor) {
offset.x += shadowView.frame.origin.x;
offset.y += shadowView.frame.origin.y;
shadowView = shadowView->_superview;
depth--;
}
if (ancestor != shadowView) {
return CGRectNull;
}
return (CGRect){offset, self.frame.size};
}
- (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;
}
- (instancetype)init
{
if ((self = [super init])) {
_frame = CGRectMake(0, 0, CSSUndefined, CSSUndefined);
for (unsigned int ii = 0; ii < META_PROP_COUNT; ii++) {
_paddingMetaProps[ii] = CSSUndefined;
_marginMetaProps[ii] = CSSUndefined;
_borderMetaProps[ii] = CSSUndefined;
}
_newView = YES;
_propagationLifecycle = RCTUpdateLifecycleUninitialized;
_textLifecycle = RCTUpdateLifecycleUninitialized;
_reactSubviews = [NSMutableArray array];
_cssNode = CSSNodeNew();
CSSNodeSetContext(_cssNode, (__bridge void *)self);
CSSNodeSetPrintFunc(_cssNode, RCTPrint);
}
return self;
}
- (BOOL)isReactRootView
{
return RCTIsReactRootView(self.reactTag);
}
- (void)dealloc
{
CSSNodeFree(_cssNode);
}
- (BOOL)isCSSLeafNode
{
return NO;
}
- (void)dirtyPropagation
{
if (_propagationLifecycle != RCTUpdateLifecycleDirtied) {
_propagationLifecycle = RCTUpdateLifecycleDirtied;
[_superview dirtyPropagation];
}
}
- (BOOL)isPropagationDirty
{
return _propagationLifecycle != RCTUpdateLifecycleComputed;
}
- (void)dirtyText
{
if (_textLifecycle != RCTUpdateLifecycleDirtied) {
_textLifecycle = RCTUpdateLifecycleDirtied;
[_superview dirtyText];
}
}
- (BOOL)isTextDirty
{
return _textLifecycle != RCTUpdateLifecycleComputed;
}
- (void)setTextComputed
{
_textLifecycle = RCTUpdateLifecycleComputed;
}
- (void)insertReactSubview:(RCTShadowView *)subview atIndex:(NSInteger)atIndex
{
[_reactSubviews insertObject:subview atIndex:atIndex];
if (![self isCSSLeafNode]) {
CSSNodeInsertChild(_cssNode, subview.cssNode, atIndex);
}
subview->_superview = self;
_didUpdateSubviews = YES;
[self dirtyText];
[self dirtyPropagation];
}
- (void)removeReactSubview:(RCTShadowView *)subview
{
[subview dirtyText];
[subview dirtyPropagation];
_didUpdateSubviews = YES;
subview->_superview = nil;
[_reactSubviews removeObject:subview];
if (![self isCSSLeafNode]) {
CSSNodeRemoveChild(_cssNode, subview.cssNode);
}
}
- (NSArray<RCTShadowView *> *)reactSubviews
{
return _reactSubviews;
}
- (RCTShadowView *)reactSuperview
{
return _superview;
}
- (NSNumber *)reactTagAtPoint:(CGPoint)point
{
for (RCTShadowView *shadowView in _reactSubviews) {
if (CGRectContainsPoint(shadowView.frame, point)) {
CGPoint relativePoint = point;
CGPoint origin = shadowView.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.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;
}
// Margin
#define RCT_MARGIN_PROPERTY(prop, metaProp) \
- (void)setMargin##prop:(CGFloat)value \
{ \
_marginMetaProps[META_PROP_##metaProp] = value; \
_recomputeMargin = YES; \
} \
- (CGFloat)margin##prop \
{ \
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)
// Padding
#define RCT_PADDING_PROPERTY(prop, metaProp) \
- (void)setPadding##prop:(CGFloat)value \
{ \
_paddingMetaProps[META_PROP_##metaProp] = value; \
_recomputePadding = YES; \
} \
- (CGFloat)padding##prop \
{ \
return _paddingMetaProps[META_PROP_##metaProp]; \
}
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)
- (UIEdgeInsets)paddingAsInsets
{
if (CSSNodeLayoutGetDirection(_cssNode) == CSSDirectionRTL) {
return (UIEdgeInsets){
CSSNodeStyleGetPaddingTop(_cssNode),
!isUndefined(CSSNodeStyleGetPaddingEnd(_cssNode)) ?
CSSNodeStyleGetPaddingEnd(_cssNode) :
CSSNodeStyleGetPaddingLeft(_cssNode),
CSSNodeStyleGetPaddingBottom(_cssNode),
!isUndefined(CSSNodeStyleGetPaddingStart(_cssNode)) ?
CSSNodeStyleGetPaddingStart(_cssNode) :
CSSNodeStyleGetPaddingRight(_cssNode)
};
} else {
return (UIEdgeInsets){
CSSNodeStyleGetPaddingTop(_cssNode),
!isUndefined(CSSNodeStyleGetPaddingStart(_cssNode)) ?
CSSNodeStyleGetPaddingStart(_cssNode) :
CSSNodeStyleGetPaddingLeft(_cssNode),
CSSNodeStyleGetPaddingBottom(_cssNode),
!isUndefined(CSSNodeStyleGetPaddingEnd(_cssNode)) ?
CSSNodeStyleGetPaddingEnd(_cssNode) :
CSSNodeStyleGetPaddingRight(_cssNode)
};
}
}
// Border
#define RCT_BORDER_PROPERTY(prop, metaProp) \
- (void)setBorder##prop##Width:(CGFloat)value \
{ \
_borderMetaProps[META_PROP_##metaProp] = value; \
_recomputeBorder = YES; \
} \
- (CGFloat)border##prop##Width \
{ \
return _borderMetaProps[META_PROP_##metaProp]; \
}
RCT_BORDER_PROPERTY(, ALL)
RCT_BORDER_PROPERTY(Top, TOP)
RCT_BORDER_PROPERTY(Left, LEFT)
RCT_BORDER_PROPERTY(Bottom, BOTTOM)
RCT_BORDER_PROPERTY(Right, RIGHT)
// Dimensions
#define RCT_DIMENSION_PROPERTY(setProp, getProp, cssProp) \
- (void)set##setProp:(CGFloat)value \
{ \
CSSNodeStyleSet##cssProp(_cssNode, value); \
[self dirtyText]; \
} \
- (CGFloat)getProp \
{ \
return CSSNodeStyleGet##cssProp(_cssNode); \
}
RCT_DIMENSION_PROPERTY(Width, width, Width)
RCT_DIMENSION_PROPERTY(Height, height, Height)
RCT_DIMENSION_PROPERTY(MinWidth, minWidth, MinWidth)
RCT_DIMENSION_PROPERTY(MinHeight, minHeight, MinHeight)
RCT_DIMENSION_PROPERTY(MaxWidth, maxWidth, MaxWidth)
RCT_DIMENSION_PROPERTY(MaxHeight, maxHeight, MaxHeight)
// Position
RCT_DIMENSION_PROPERTY(Top, top, PositionTop)
RCT_DIMENSION_PROPERTY(Right, right, PositionEnd)
RCT_DIMENSION_PROPERTY(Bottom, bottom, PositionBottom)
RCT_DIMENSION_PROPERTY(Left, left, PositionStart)
- (void)setFrame:(CGRect)frame
{
CSSNodeStyleSetPositionLeft(_cssNode, CGRectGetMinX(frame));
CSSNodeStyleSetPositionTop(_cssNode, CGRectGetMinY(frame));
CSSNodeStyleSetWidth(_cssNode, CGRectGetWidth(frame));
CSSNodeStyleSetHeight(_cssNode, CGRectGetHeight(frame));
}
static inline void RCTAssignSuggestedDimension(CSSNodeRef cssNode, CSSDimension dimension, CGFloat amount)
{
if (amount != UIViewNoIntrinsicMetric) {
switch (dimension) {
case CSSDimensionWidth:
if (isnan(CSSNodeStyleGetWidth(cssNode))) {
CSSNodeStyleSetWidth(cssNode, amount);
}
break;
case CSSDimensionHeight:
if (isnan(CSSNodeStyleGetHeight(cssNode))) {
CSSNodeStyleSetHeight(cssNode, amount);
}
break;
}
}
}
- (void)setIntrinsicContentSize:(CGSize)size
{
if (CSSNodeStyleGetFlex(_cssNode) == 0) {
RCTAssignSuggestedDimension(_cssNode, CSSDimensionHeight, size.height);
RCTAssignSuggestedDimension(_cssNode, CSSDimensionWidth, size.width);
}
}
- (void)setTopLeft:(CGPoint)topLeft
{
CSSNodeStyleSetPositionLeft(_cssNode, topLeft.x);
CSSNodeStyleSetPositionTop(_cssNode, topLeft.y);
}
- (void)setSize:(CGSize)size
{
CSSNodeStyleSetWidth(_cssNode, size.width);
CSSNodeStyleSetHeight(_cssNode, size.height);
}
// Flex
#define RCT_STYLE_PROPERTY(setProp, getProp, cssProp, type) \
- (void)set##setProp:(type)value \
{ \
CSSNodeStyleSet##cssProp(_cssNode, value); \
} \
- (type)getProp \
{ \
return CSSNodeStyleGet##cssProp(_cssNode); \
}
RCT_STYLE_PROPERTY(Flex, flex, Flex, CGFloat)
RCT_STYLE_PROPERTY(FlexDirection, flexDirection, FlexDirection, CSSFlexDirection)
RCT_STYLE_PROPERTY(JustifyContent, justifyContent, JustifyContent, CSSJustify)
RCT_STYLE_PROPERTY(AlignSelf, alignSelf, AlignSelf, CSSAlign)
RCT_STYLE_PROPERTY(AlignItems, alignItems, AlignItems, CSSAlign)
RCT_STYLE_PROPERTY(Position, position, PositionType, CSSPositionType)
RCT_STYLE_PROPERTY(FlexWrap, flexWrap, FlexWrap, CSSWrapType)
- (void)setBackgroundColor:(UIColor *)color
{
_backgroundColor = color;
[self dirtyPropagation];
}
- (void)setZIndex:(NSInteger)zIndex
{
_zIndex = zIndex;
if (_superview) {
// Changing zIndex means the subview order of the parent needs updating
_superview->_didUpdateSubviews = YES;
[_superview dirtyPropagation];
}
}
- (void)didUpdateReactSubviews
{
// Does nothing by default
}
- (void)didSetProps:(__unused NSArray<NSString *> *)changedProps
{
if (_recomputePadding) {
RCTProcessMetaPropsPadding(_paddingMetaProps, _cssNode);
}
if (_recomputeMargin) {
RCTProcessMetaPropsMargin(_marginMetaProps, _cssNode);
}
if (_recomputeBorder) {
RCTProcessMetaPropsBorder(_borderMetaProps, _cssNode);
}
_recomputeMargin = NO;
_recomputePadding = NO;
_recomputeBorder = NO;
}
@end