2015-03-23 22:07:33 +00:00
/ * *
* 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 .
* /
2015-01-30 01:10:49 +00:00
# import "RCTShadowView.h"
# import "RCTConvert.h"
# import "RCTLog.h"
# import "RCTUtils.h"
2015-05-28 16:29:27 +00:00
# import "UIView+React.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"
2015-01-30 01:10:49 +00:00
typedef void ( ^ RCTActionBlock ) ( RCTShadowView * shadowViewSelf , id value ) ;
typedef void ( ^ RCTResetActionBlock ) ( RCTShadowView * shadowViewSelf ) ;
2015-11-14 18:25:00 +00:00
static NSString * const RCTBackgroundColorProp = @ "backgroundColor" ;
2015-01-30 01:10:49 +00:00
2015-08-24 10:14:33 +00:00
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 _HORIZONTAL ,
META_PROP _VERTICAL ,
META_PROP _ALL ,
META_PROP _COUNT ,
2015-08-24 10:14:33 +00:00
} ;
2015-01-30 01:10:49 +00:00
@ implementation RCTShadowView
{
2015-03-02 19:36:55 +00:00
RCTUpdateLifecycle _propagationLifecycle ;
RCTUpdateLifecycle _textLifecycle ;
2015-01-30 01:10:49 +00:00
NSDictionary * _lastParentProperties ;
2015-11-03 22:45:46 +00:00
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 ;
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
BOOL _didUpdateSubviews ;
2015-03-02 19:36:55 +00:00
float _paddingMetaProps [ META_PROP _COUNT ] ;
float _marginMetaProps [ META_PROP _COUNT ] ;
2015-07-30 13:20:28 +00:00
float _borderMetaProps [ META_PROP _COUNT ] ;
2015-01-30 01:10:49 +00:00
}
@ synthesize reactTag = _reactTag ;
2016-07-20 13:40:26 +00:00
// cssNode api
2015-01-30 01:10:49 +00:00
2016-10-27 17:52:11 +00:00
static void RCTPrint ( CSSNodeRef node )
2015-01-30 01:10:49 +00:00
{
2016-10-27 17:52:11 +00:00
RCTShadowView * shadowView = ( __bridge RCTShadowView * ) CSSNodeGetContext ( node ) ;
2015-03-17 20:42:44 +00:00
printf ( "%s(%zd), " , shadowView . viewName . UTF8String , shadowView . reactTag . integerValue ) ;
2015-01-30 01:10:49 +00:00
}
// Enforces precedence rules , e . g . marginLeft > marginHorizontal > margin .
2016-07-20 15:46:00 +00:00
# define DEFINE_PROCESS _META _PROPS ( type ) \
static void RCTProcessMetaProps # # type ( const float metaProps [ META_PROP _COUNT ] , CSSNodeRef node ) { \
2016-08-05 13:27:39 +00:00
if ( ! CSSValueIsUndefined ( metaProps [ META_PROP _LEFT ] ) ) { \
2016-08-15 16:15:10 +00:00
CSSNodeStyleSet # # type ( node , CSSEdgeStart , metaProps [ META_PROP _LEFT ] ) ; \
2016-08-05 13:27:39 +00:00
} else if ( ! CSSValueIsUndefined ( metaProps [ META_PROP _HORIZONTAL ] ) ) { \
2016-08-15 16:15:10 +00:00
CSSNodeStyleSet # # type ( node , CSSEdgeStart , metaProps [ META_PROP _HORIZONTAL ] ) ; \
2016-08-05 13:27:39 +00:00
} else if ( ! CSSValueIsUndefined ( metaProps [ META_PROP _ALL ] ) ) { \
2016-08-15 16:15:10 +00:00
CSSNodeStyleSet # # type ( node , CSSEdgeStart , metaProps [ META_PROP _ALL ] ) ; \
2016-07-20 15:46:00 +00:00
} else { \
2016-08-15 16:15:10 +00:00
CSSNodeStyleSet # # type ( node , CSSEdgeStart , 0 ) ; \
2016-07-20 15:46:00 +00:00
} \
\
2016-08-05 13:27:39 +00:00
if ( ! CSSValueIsUndefined ( metaProps [ META_PROP _RIGHT ] ) ) { \
2016-08-15 16:15:10 +00:00
CSSNodeStyleSet # # type ( node , CSSEdgeEnd , metaProps [ META_PROP _RIGHT ] ) ; \
2016-08-05 13:27:39 +00:00
} else if ( ! CSSValueIsUndefined ( metaProps [ META_PROP _HORIZONTAL ] ) ) { \
2016-08-15 16:15:10 +00:00
CSSNodeStyleSet # # type ( node , CSSEdgeEnd , metaProps [ META_PROP _HORIZONTAL ] ) ; \
2016-08-05 13:27:39 +00:00
} else if ( ! CSSValueIsUndefined ( metaProps [ META_PROP _ALL ] ) ) { \
2016-08-15 16:15:10 +00:00
CSSNodeStyleSet # # type ( node , CSSEdgeEnd , metaProps [ META_PROP _ALL ] ) ; \
2016-07-20 15:46:00 +00:00
} else { \
2016-08-15 16:15:10 +00:00
CSSNodeStyleSet # # type ( node , CSSEdgeEnd , 0 ) ; \
2016-07-20 15:46:00 +00:00
} \
\
2016-08-05 13:27:39 +00:00
if ( ! CSSValueIsUndefined ( metaProps [ META_PROP _TOP ] ) ) { \
2016-08-15 16:15:10 +00:00
CSSNodeStyleSet # # type ( node , CSSEdgeTop , metaProps [ META_PROP _TOP ] ) ; \
2016-08-05 13:27:39 +00:00
} else if ( ! CSSValueIsUndefined ( metaProps [ META_PROP _VERTICAL ] ) ) { \
2016-08-15 16:15:10 +00:00
CSSNodeStyleSet # # type ( node , CSSEdgeTop , metaProps [ META_PROP _VERTICAL ] ) ; \
2016-08-05 13:27:39 +00:00
} else if ( ! CSSValueIsUndefined ( metaProps [ META_PROP _ALL ] ) ) { \
2016-08-15 16:15:10 +00:00
CSSNodeStyleSet # # type ( node , CSSEdgeTop , metaProps [ META_PROP _ALL ] ) ; \
2016-07-20 15:46:00 +00:00
} else { \
2016-08-15 16:15:10 +00:00
CSSNodeStyleSet # # type ( node , CSSEdgeTop , 0 ) ; \
2016-07-20 15:46:00 +00:00
} \
\
2016-08-05 13:27:39 +00:00
if ( ! CSSValueIsUndefined ( metaProps [ META_PROP _BOTTOM ] ) ) { \
2016-08-15 16:15:10 +00:00
CSSNodeStyleSet # # type ( node , CSSEdgeBottom , metaProps [ META_PROP _BOTTOM ] ) ; \
2016-08-05 13:27:39 +00:00
} else if ( ! CSSValueIsUndefined ( metaProps [ META_PROP _VERTICAL ] ) ) { \
2016-08-15 16:15:10 +00:00
CSSNodeStyleSet # # type ( node , CSSEdgeBottom , metaProps [ META_PROP _VERTICAL ] ) ; \
2016-08-05 13:27:39 +00:00
} else if ( ! CSSValueIsUndefined ( metaProps [ META_PROP _ALL ] ) ) { \
2016-08-15 16:15:10 +00:00
CSSNodeStyleSet # # type ( node , CSSEdgeBottom , metaProps [ META_PROP _ALL ] ) ; \
2016-07-20 15:46:00 +00:00
} else { \
2016-08-15 16:15:10 +00:00
CSSNodeStyleSet # # type ( node , CSSEdgeBottom , 0 ) ; \
2016-07-20 15:46:00 +00:00
} \
}
DEFINE_PROCESS _META _PROPS ( Padding ) ;
DEFINE_PROCESS _META _PROPS ( Margin ) ;
DEFINE_PROCESS _META _PROPS ( Border ) ;
2015-01-30 01:10:49 +00:00
// 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
2016-11-10 22:59:15 +00:00
// absoluteRight = round ( absolutePosition . x + viewPosition . left + viewSize . left ) + round ( 106.667 + 0 + 106.667 ) = 213.5
2015-01-30 01:10:49 +00:00
// 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 .
2016-07-20 15:46:00 +00:00
- ( void ) applyLayoutNode : ( CSSNodeRef ) node
2015-11-03 22:45:46 +00:00
viewsWithNewFrame : ( NSMutableSet < RCTShadowView * > * ) viewsWithNewFrame
2015-06-01 15:34:09 +00:00
absolutePosition : ( CGPoint ) absolutePosition
2015-01-30 01:10:49 +00:00
{
2016-08-01 12:24:18 +00:00
if ( ! CSSNodeGetHasNewLayout ( node ) ) {
2015-01-30 01:10:49 +00:00
return ;
}
2016-08-01 12:24:18 +00:00
CSSNodeSetHasNewLayout ( node , false ) ;
2015-01-30 01:10:49 +00:00
2016-10-28 19:08:59 +00:00
# if RCT_DEBUG
// This works around a breaking change in css - layout where setting flexBasis needs to be set explicitly , instead of relying on flex to propagate .
// We check for it by seeing if a width / height is provided along with a flexBasis of 0 and the width / height is laid out as 0.
if ( ( ! CSSValueIsUndefined ( CSSNodeStyleGetFlexBasis ( node ) ) && CSSNodeStyleGetFlexBasis ( node ) = = 0 ) &&
( ( ! CSSValueIsUndefined ( CSSNodeStyleGetWidth ( node ) ) && CSSNodeStyleGetWidth ( node ) > 0 && CSSNodeLayoutGetWidth ( node ) = = 0 ) ||
( ! CSSValueIsUndefined ( CSSNodeStyleGetHeight ( node ) ) && CSSNodeStyleGetHeight ( node ) > 0 && CSSNodeLayoutGetHeight ( node ) = = 0 ) ) ) {
RCTLogError ( @ "View was rendered with explicitly set width/height but with a 0 flexBasis. (This might be fixed by changing flex: to flexGrow:) View: %@" , self ) ;
}
# endif
2016-11-10 22:59:15 +00:00
CGPoint absoluteTopLeft = {
2016-07-20 15:46:00 +00:00
absolutePosition . x + CSSNodeLayoutGetLeft ( node ) ,
absolutePosition . y + CSSNodeLayoutGetTop ( node )
2015-01-30 01:10:49 +00:00
} ;
2015-03-02 19:36:55 +00:00
2016-11-10 22:59:15 +00:00
CGPoint absoluteBottomRight = {
absolutePosition . x + CSSNodeLayoutGetLeft ( node ) + CSSNodeLayoutGetWidth ( node ) ,
absolutePosition . y + CSSNodeLayoutGetTop ( node ) + CSSNodeLayoutGetHeight ( node )
2015-01-30 01:10:49 +00:00
} ;
2015-03-02 19:36:55 +00:00
2016-11-10 22:59:15 +00:00
CGRect frame = { {
2016-07-20 15:46:00 +00:00
RCTRoundPixelValue ( CSSNodeLayoutGetLeft ( node ) ) ,
RCTRoundPixelValue ( CSSNodeLayoutGetTop ( node ) ) ,
2015-03-17 20:42:44 +00:00
} , {
2016-11-10 22:59:15 +00:00
RCTRoundPixelValue ( absoluteBottomRight . x - absoluteTopLeft . x ) ,
RCTRoundPixelValue ( absoluteBottomRight . y - absoluteTopLeft . y )
2015-03-17 20:42:44 +00:00
} } ;
2015-01-30 01:10:49 +00:00
if ( ! CGRectEqualToRect ( frame , _frame ) ) {
_frame = frame ;
[ viewsWithNewFrame addObject : self ] ;
}
2016-07-20 15:46:00 +00:00
absolutePosition . x + = CSSNodeLayoutGetLeft ( node ) ;
absolutePosition . y + = CSSNodeLayoutGetTop ( node ) ;
2015-01-30 01:10:49 +00:00
2016-05-31 17:18:37 +00:00
[ self applyLayoutToChildren : node viewsWithNewFrame : viewsWithNewFrame absolutePosition : absolutePosition ] ;
}
2016-07-20 15:46:00 +00:00
- ( void ) applyLayoutToChildren : ( CSSNodeRef ) node
2016-05-31 17:18:37 +00:00
viewsWithNewFrame : ( NSMutableSet < RCTShadowView * > * ) viewsWithNewFrame
absolutePosition : ( CGPoint ) absolutePosition
{
2016-07-20 20:26:57 +00:00
for ( unsigned int i = 0 ; i < CSSNodeChildCount ( node ) ; + + i ) {
2015-01-30 01:10:49 +00:00
RCTShadowView * child = ( RCTShadowView * ) _reactSubviews [ i ] ;
2016-07-20 20:26:57 +00:00
[ child applyLayoutNode : CSSNodeGetChild ( node , i )
2015-06-01 15:34:09 +00:00
viewsWithNewFrame : viewsWithNewFrame
absolutePosition : absolutePosition ] ;
2015-01-30 01:10:49 +00:00
}
}
2015-11-14 18:25:00 +00:00
- ( NSDictionary < NSString * , id > * ) processUpdatedProperties : ( NSMutableSet < RCTApplierBlock > * ) applierBlocks
parentProperties : ( NSDictionary < NSString * , id > * ) parentProperties
2015-01-30 01:10:49 +00:00
{
2015-06-01 15:34:09 +00:00
// TODO : we always refresh all propagated properties when propagation is
// dirtied , but really we should track which properties have changed and
// only update those .
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
if ( _didUpdateSubviews ) {
_didUpdateSubviews = NO ;
[ self didUpdateReactSubviews ] ;
[ applierBlocks addObject : ^ ( NSDictionary < NSNumber * , UIView * > * viewRegistry ) {
2016-07-07 19:36:56 +00:00
UIView * view = viewRegistry [ self -> _reactTag ] ;
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
[ view clearSortedSubviews ] ;
[ view didUpdateReactSubviews ] ;
} ] ;
}
2015-05-28 16:29:27 +00:00
if ( ! _backgroundColor ) {
2015-01-30 01:10:49 +00:00
UIColor * parentBackgroundColor = parentProperties [ RCTBackgroundColorProp ] ;
2015-05-28 16:29:27 +00:00
if ( parentBackgroundColor ) {
2015-11-14 18:25:00 +00:00
[ applierBlocks addObject : ^ ( NSDictionary < NSNumber * , UIView * > * viewRegistry ) {
2016-07-07 19:36:56 +00:00
UIView * view = viewRegistry [ self -> _reactTag ] ;
2015-05-28 16:29:27 +00:00
[ view reactSetInheritedBackgroundColor : parentBackgroundColor ] ;
2015-01-30 01:10:49 +00:00
} ] ;
}
2015-05-28 16:29:27 +00:00
} else {
2015-01-30 01:10:49 +00:00
// Update parent properties for children
2015-11-14 18:25:00 +00:00
NSMutableDictionary < NSString * , id > * properties = [ NSMutableDictionary dictionaryWithDictionary : parentProperties ] ;
2015-01-30 01:10:49 +00:00
CGFloat alpha = CGColorGetAlpha ( _backgroundColor . CGColor ) ;
2015-05-26 19:40:26 +00:00
if ( alpha < 1.0 ) {
2015-05-28 16:29:27 +00:00
// If bg is non - opaque , don ' t propagate further
2015-01-30 01:10:49 +00:00
properties [ RCTBackgroundColorProp ] = [ UIColor clearColor ] ;
} else {
properties [ RCTBackgroundColorProp ] = _backgroundColor ;
}
return properties ;
}
return parentProperties ;
}
2015-11-03 22:45:46 +00:00
- ( void ) collectUpdatedProperties : ( NSMutableSet < RCTApplierBlock > * ) applierBlocks
2015-11-14 18:25:00 +00:00
parentProperties : ( NSDictionary < NSString * , id > * ) parentProperties
2015-01-30 01:10:49 +00:00
{
2015-03-02 19:36:55 +00:00
if ( _propagationLifecycle = = RCTUpdateLifecycleComputed && [ parentProperties isEqualToDictionary : _lastParentProperties ] ) {
2015-01-30 01:10:49 +00:00
return ;
}
2015-03-02 19:36:55 +00:00
_propagationLifecycle = RCTUpdateLifecycleComputed ;
2015-01-30 01:10:49 +00:00
_lastParentProperties = parentProperties ;
2015-11-14 18:25:00 +00:00
NSDictionary < NSString * , id > * nextProps = [ self processUpdatedProperties : applierBlocks parentProperties : parentProperties ] ;
2015-01-30 01:10:49 +00:00
for ( RCTShadowView * child in _reactSubviews ) {
[ child collectUpdatedProperties : applierBlocks parentProperties : nextProps ] ;
}
}
2016-05-31 17:18:37 +00:00
- ( 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 ) ) {
2016-07-20 15:46:00 +00:00
CSSNodeStyleSetPositionType ( _cssNode , CSSPositionTypeAbsolute ) ;
CSSNodeStyleSetWidth ( _cssNode , frame . size . width ) ;
CSSNodeStyleSetHeight ( _cssNode , frame . size . height ) ;
2016-08-15 16:15:10 +00:00
CSSNodeStyleSetPosition ( _cssNode , CSSEdgeLeft , frame . origin . x ) ;
CSSNodeStyleSetPosition ( _cssNode , CSSEdgeTop , frame . origin . y ) ;
2016-05-31 17:18:37 +00:00
}
2016-07-20 15:46:00 +00:00
CSSNodeCalculateLayout ( _cssNode , frame . size . width , frame . size . height , CSSDirectionInherit ) ;
2016-05-31 17:18:37 +00:00
[ self applyLayoutNode : _cssNode viewsWithNewFrame : viewsWithNewFrame absolutePosition : absolutePosition ] ;
}
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 ) {
2015-06-01 15:34:09 +00:00
offset . x + = shadowView . frame . origin . x ;
offset . y + = shadowView . 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 ;
}
2015-06-01 15:34:09 +00:00
return ( CGRect ) { offset , self . frame . size } ;
2015-01-30 01:10:49 +00:00
}
2016-08-03 11:01:45 +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 ] ) ) {
2015-03-02 19:36:55 +00:00
2016-07-20 13:40:26 +00:00
_frame = CGRectMake ( 0 , 0 , CSSUndefined , CSSUndefined ) ;
2015-01-30 01:10:49 +00:00
2015-08-24 10:14:33 +00:00
for ( unsigned int ii = 0 ; ii < META_PROP _COUNT ; ii + + ) {
2016-07-20 13:40:26 +00:00
_paddingMetaProps [ ii ] = CSSUndefined ;
_marginMetaProps [ ii ] = CSSUndefined ;
_borderMetaProps [ ii ] = CSSUndefined ;
2015-01-30 01:10:49 +00:00
}
_newView = YES ;
2015-03-02 19:36:55 +00:00
_propagationLifecycle = RCTUpdateLifecycleUninitialized ;
_textLifecycle = RCTUpdateLifecycleUninitialized ;
2015-01-30 01:10:49 +00:00
_reactSubviews = [ NSMutableArray array ] ;
2016-07-20 13:40:26 +00:00
_cssNode = CSSNodeNew ( ) ;
2016-07-20 15:46:00 +00:00
CSSNodeSetContext ( _cssNode , ( __bridge void * ) self ) ;
CSSNodeSetPrintFunc ( _cssNode , 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
{
2016-07-20 13:40:26 +00:00
CSSNodeFree ( _cssNode ) ;
2015-01-30 01:10:49 +00:00
}
2016-07-20 09:49:13 +00:00
- ( BOOL ) isCSSLeafNode
{
return NO ;
}
2015-01-30 01:10:49 +00:00
- ( void ) dirtyPropagation
{
2015-03-02 19:36:55 +00:00
if ( _propagationLifecycle ! = RCTUpdateLifecycleDirtied ) {
_propagationLifecycle = RCTUpdateLifecycleDirtied ;
2015-01-30 01:10:49 +00:00
[ _superview dirtyPropagation ] ;
}
}
2015-02-19 01:39:09 +00:00
- ( BOOL ) isPropagationDirty
{
2015-03-02 19:36:55 +00:00
return _propagationLifecycle ! = RCTUpdateLifecycleComputed ;
2015-02-19 01:39:09 +00:00
}
2015-01-30 01:10:49 +00:00
- ( void ) dirtyText
{
2015-03-02 19:36:55 +00:00
if ( _textLifecycle ! = RCTUpdateLifecycleDirtied ) {
_textLifecycle = RCTUpdateLifecycleDirtied ;
2015-01-30 01:10:49 +00:00
[ _superview dirtyText ] ;
}
}
- ( BOOL ) isTextDirty
{
2015-03-02 19:36:55 +00:00
return _textLifecycle ! = RCTUpdateLifecycleComputed ;
2015-01-30 01:10:49 +00:00
}
- ( void ) setTextComputed
{
2015-03-02 19:36:55 +00:00
_textLifecycle = RCTUpdateLifecycleComputed ;
2015-01-30 01:10:49 +00:00
}
- ( void ) insertReactSubview : ( RCTShadowView * ) subview atIndex : ( NSInteger ) atIndex
{
[ _reactSubviews insertObject : subview atIndex : atIndex ] ;
2016-07-20 20:26:57 +00:00
if ( ! [ self isCSSLeafNode ] ) {
2016-08-09 10:53:59 +00:00
CSSNodeInsertChild ( _cssNode , subview . cssNode , ( uint32_t ) atIndex ) ;
2016-07-20 20:26:57 +00:00
}
2015-01-30 01:10:49 +00:00
subview -> _superview = self ;
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
_didUpdateSubviews = YES ;
2015-01-30 01:10:49 +00:00
[ self dirtyText ] ;
[ self dirtyPropagation ] ;
}
- ( void ) removeReactSubview : ( RCTShadowView * ) subview
{
[ subview dirtyText ] ;
[ subview dirtyPropagation ] ;
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
_didUpdateSubviews = YES ;
2015-01-30 01:10:49 +00:00
subview -> _superview = nil ;
[ _reactSubviews removeObject : subview ] ;
2016-07-20 20:26:57 +00:00
if ( ! [ self isCSSLeafNode ] ) {
CSSNodeRemoveChild ( _cssNode , subview . cssNode ) ;
}
2015-01-30 01:10:49 +00:00
}
2015-11-03 22:45:46 +00:00
- ( NSArray < RCTShadowView * > * ) reactSubviews
2015-01-30 01:10:49 +00:00
{
return _reactSubviews ;
}
2015-03-06 17:54:10 +00:00
- ( RCTShadowView * ) reactSuperview
{
return _superview ;
}
2015-02-06 23:45:28 +00:00
- ( 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 ;
}
2015-06-15 19:05:04 +00:00
- ( NSString * ) description
{
NSString * description = super . description ;
2015-07-17 10:53:15 +00:00
description = [ [ description substringToIndex : description . length - 1 ] stringByAppendingFormat : @ "; viewName: %@; reactTag: %@; frame: %@>" , self . viewName , self . reactTag , NSStringFromCGRect ( self . frame ) ] ;
return description ;
2015-06-15 19:05:04 +00:00
}
- ( 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 ) \
2016-10-26 20:12:26 +00:00
- ( void ) setMargin # # prop : ( float ) value \
2015-01-30 01:10:49 +00:00
{ \
_marginMetaProps [ META_PROP _ # # metaProp ] = value ; \
_recomputeMargin = YES ; \
} \
2016-10-26 20:12:26 +00:00
- ( float ) 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 )
// Padding
# define RCT_PADDING _PROPERTY ( prop , metaProp ) \
2016-10-26 20:12:26 +00:00
- ( void ) setPadding # # prop : ( float ) value \
2015-01-30 01:10:49 +00:00
{ \
_paddingMetaProps [ META_PROP _ # # metaProp ] = value ; \
_recomputePadding = YES ; \
} \
2016-10-26 20:12:26 +00:00
- ( float ) padding # # prop \
2015-01-30 01:10:49 +00:00
{ \
2015-02-19 01:46:39 +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 )
2015-02-19 01:46:39 +00:00
- ( UIEdgeInsets ) paddingAsInsets
{
2016-07-29 00:27:40 +00:00
if ( CSSNodeLayoutGetDirection ( _cssNode ) = = CSSDirectionRTL ) {
return ( UIEdgeInsets ) {
2016-08-15 16:15:10 +00:00
CSSNodeStyleGetPadding ( _cssNode , CSSEdgeTop ) ,
! CSSValueIsUndefined ( CSSNodeStyleGetPadding ( _cssNode , CSSEdgeEnd ) ) ?
CSSNodeStyleGetPadding ( _cssNode , CSSEdgeEnd ) :
CSSNodeStyleGetPadding ( _cssNode , CSSEdgeLeft ) ,
CSSNodeStyleGetPadding ( _cssNode , CSSEdgeBottom ) ,
! CSSValueIsUndefined ( CSSNodeStyleGetPadding ( _cssNode , CSSEdgeStart ) ) ?
CSSNodeStyleGetPadding ( _cssNode , CSSEdgeStart ) :
CSSNodeStyleGetPadding ( _cssNode , CSSEdgeRight )
2016-07-29 00:27:40 +00:00
} ;
} else {
return ( UIEdgeInsets ) {
2016-08-15 16:15:10 +00:00
CSSNodeStyleGetPadding ( _cssNode , CSSEdgeTop ) ,
! CSSValueIsUndefined ( CSSNodeStyleGetPadding ( _cssNode , CSSEdgeStart ) ) ?
CSSNodeStyleGetPadding ( _cssNode , CSSEdgeStart ) :
CSSNodeStyleGetPadding ( _cssNode , CSSEdgeLeft ) ,
CSSNodeStyleGetPadding ( _cssNode , CSSEdgeBottom ) ,
! CSSValueIsUndefined ( CSSNodeStyleGetPadding ( _cssNode , CSSEdgeEnd ) ) ?
CSSNodeStyleGetPadding ( _cssNode , CSSEdgeEnd ) :
CSSNodeStyleGetPadding ( _cssNode , CSSEdgeRight )
2016-07-29 00:27:40 +00:00
} ;
}
2015-02-19 01:46:39 +00:00
}
2015-01-30 01:10:49 +00:00
// Border
2015-02-19 01:46:39 +00:00
# define RCT_BORDER _PROPERTY ( prop , metaProp ) \
2016-10-26 20:12:26 +00:00
- ( void ) setBorder # # prop # # Width : ( float ) value \
2015-02-19 01:46:39 +00:00
{ \
2015-07-30 13:20:28 +00:00
_borderMetaProps [ META_PROP _ # # metaProp ] = value ; \
_recomputeBorder = YES ; \
2015-02-19 01:46:39 +00:00
} \
2016-10-26 20:12:26 +00:00
- ( float ) border # # prop # # Width \
2015-02-19 01:46:39 +00:00
{ \
2015-07-30 13:20:28 +00:00
return _borderMetaProps [ META_PROP _ # # metaProp ] ; \
2015-02-19 01:46:39 +00:00
}
2015-07-30 13:20:28 +00:00
RCT_BORDER _PROPERTY ( , ALL )
2015-02-19 01:46:39 +00:00
RCT_BORDER _PROPERTY ( Top , TOP )
RCT_BORDER _PROPERTY ( Left , LEFT )
RCT_BORDER _PROPERTY ( Bottom , BOTTOM )
RCT_BORDER _PROPERTY ( Right , RIGHT )
2015-01-30 01:10:49 +00:00
// Dimensions
2016-06-15 16:44:34 +00:00
2016-07-20 15:46:00 +00:00
# define RCT_DIMENSION _PROPERTY ( setProp , getProp , cssProp ) \
2016-10-26 20:12:26 +00:00
- ( void ) set # # setProp : ( float ) value \
2016-06-15 16:44:34 +00:00
{ \
2016-07-20 15:46:00 +00:00
CSSNodeStyleSet # # cssProp ( _cssNode , value ) ; \
2016-06-15 16:44:34 +00:00
[ self dirtyText ] ; \
} \
2016-10-26 20:12:26 +00:00
- ( float ) getProp \
2016-06-15 16:44:34 +00:00
{ \
2016-07-20 15:46:00 +00:00
return CSSNodeStyleGet # # cssProp ( _cssNode ) ; \
2015-11-17 18:57:00 +00:00
}
2016-07-20 15:46:00 +00:00
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 )
2015-01-30 01:10:49 +00:00
// Position
2016-08-15 16:15:10 +00:00
# define RCT_POSITION _PROPERTY ( setProp , getProp , edge ) \
2016-10-26 20:12:26 +00:00
- ( void ) set # # setProp : ( float ) value \
2016-08-15 16:15:10 +00:00
{ \
CSSNodeStyleSetPosition ( _cssNode , edge , value ) ; \
[ self dirtyText ] ; \
} \
2016-10-26 20:12:26 +00:00
- ( float ) getProp \
2016-08-15 16:15:10 +00:00
{ \
return CSSNodeStyleGetPosition ( _cssNode , edge ) ; \
}
RCT_POSITION _PROPERTY ( Top , top , CSSEdgeTop )
RCT_POSITION _PROPERTY ( Right , right , CSSEdgeEnd )
RCT_POSITION _PROPERTY ( Bottom , bottom , CSSEdgeBottom )
RCT_POSITION _PROPERTY ( Left , left , CSSEdgeStart )
2015-01-30 01:10:49 +00:00
- ( void ) setFrame : ( CGRect ) frame
{
2016-08-10 17:44:51 +00:00
if ( ! CGRectEqualToRect ( frame , _frame ) ) {
_frame = frame ;
2016-08-15 16:15:10 +00:00
CSSNodeStyleSetPosition ( _cssNode , CSSEdgeLeft , CGRectGetMinX ( frame ) ) ;
CSSNodeStyleSetPosition ( _cssNode , CSSEdgeTop , CGRectGetMinY ( frame ) ) ;
2016-08-10 17:44:51 +00:00
CSSNodeStyleSetWidth ( _cssNode , CGRectGetWidth ( frame ) ) ;
CSSNodeStyleSetHeight ( _cssNode , CGRectGetHeight ( frame ) ) ;
}
2015-01-30 01:10:49 +00:00
}
2016-07-25 10:48:50 +00:00
static inline void RCTAssignSuggestedDimension ( CSSNodeRef cssNode , CSSDimension dimension , CGFloat amount )
2016-07-20 15:46:00 +00:00
{
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 ;
}
2016-03-01 18:13:22 +00:00
}
}
- ( void ) setIntrinsicContentSize : ( CGSize ) size
{
2016-10-24 10:34:55 +00:00
if ( CSSNodeStyleGetFlexGrow ( _cssNode ) = = 0 && CSSNodeStyleGetFlexShrink ( _cssNode ) = = 0 ) {
2016-07-25 10:48:50 +00:00
RCTAssignSuggestedDimension ( _cssNode , CSSDimensionHeight , size . height ) ;
RCTAssignSuggestedDimension ( _cssNode , CSSDimensionWidth , size . width ) ;
2016-03-01 18:13:22 +00:00
}
}
2015-02-19 01:39:09 +00:00
- ( void ) setTopLeft : ( CGPoint ) topLeft
{
2016-08-15 16:15:10 +00:00
CSSNodeStyleSetPosition ( _cssNode , CSSEdgeLeft , topLeft . x ) ;
CSSNodeStyleSetPosition ( _cssNode , CSSEdgeTop , topLeft . y ) ;
2015-02-19 01:39:09 +00:00
}
- ( void ) setSize : ( CGSize ) size
{
2016-07-20 15:46:00 +00:00
CSSNodeStyleSetWidth ( _cssNode , size . width ) ;
CSSNodeStyleSetHeight ( _cssNode , size . height ) ;
2015-02-19 01:39:09 +00:00
}
2015-01-30 01:10:49 +00:00
// Flex
2016-10-26 20:12:24 +00:00
- ( void ) setFlex : ( float ) value
2016-10-24 10:34:55 +00:00
{
CSSNodeStyleSetFlex ( _cssNode , value ) ;
}
2015-01-30 01:10:49 +00:00
# define RCT_STYLE _PROPERTY ( setProp , getProp , cssProp , type ) \
- ( void ) set # # setProp : ( type ) value \
{ \
2016-07-20 15:46:00 +00:00
CSSNodeStyleSet # # cssProp ( _cssNode , value ) ; \
2015-01-30 01:10:49 +00:00
} \
- ( type ) getProp \
{ \
2016-07-20 15:46:00 +00:00
return CSSNodeStyleGet # # cssProp ( _cssNode ) ; \
2015-01-30 01:10:49 +00:00
}
2016-10-26 20:12:26 +00:00
RCT_STYLE _PROPERTY ( FlexGrow , flexGrow , FlexGrow , float )
RCT_STYLE _PROPERTY ( FlexShrink , flexShrink , FlexShrink , float )
RCT_STYLE _PROPERTY ( FlexBasis , flexBasis , FlexBasis , float )
2016-07-20 15:46:00 +00:00
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 )
2016-11-14 11:27:31 +00:00
RCT_STYLE _PROPERTY ( FlexWrap , flexWrap , FlexWrap , CSSWrap )
2016-08-30 07:58:15 +00:00
RCT_STYLE _PROPERTY ( Overflow , overflow , Overflow , CSSOverflow )
2015-01-30 01:10:49 +00:00
- ( void ) setBackgroundColor : ( UIColor * ) color
{
_backgroundColor = color ;
[ self dirtyPropagation ] ;
}
2016-06-09 16:48:56 +00:00
- ( void ) setZIndex : ( NSInteger ) zIndex
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
{
_zIndex = zIndex ;
if ( _superview ) {
// Changing zIndex means the subview order of the parent needs updating
_superview -> _didUpdateSubviews = YES ;
[ _superview dirtyPropagation ] ;
}
}
2016-06-07 07:08:16 +00:00
- ( void ) didUpdateReactSubviews
{
// Does nothing by default
}
2015-12-02 15:08:29 +00:00
- ( void ) didSetProps : ( __unused NSArray < NSString * > * ) changedProps
2015-02-19 01:39:09 +00:00
{
if ( _recomputePadding ) {
2016-07-20 15:46:00 +00:00
RCTProcessMetaPropsPadding ( _paddingMetaProps , _cssNode ) ;
2015-02-19 01:39:09 +00:00
}
if ( _recomputeMargin ) {
2016-07-20 15:46:00 +00:00
RCTProcessMetaPropsMargin ( _marginMetaProps , _cssNode ) ;
2015-02-19 01:39:09 +00:00
}
2015-07-30 13:20:28 +00:00
if ( _recomputeBorder ) {
2016-07-20 15:46:00 +00:00
RCTProcessMetaPropsBorder ( _borderMetaProps , _cssNode ) ;
2015-07-30 13:20:28 +00:00
}
2015-02-19 01:39:09 +00:00
_recomputeMargin = NO ;
_recomputePadding = NO ;
2015-07-30 13:20:28 +00:00
_recomputeBorder = NO ;
2015-02-19 01:39:09 +00:00
}
2015-01-30 01:10:49 +00:00
@ end