Simplifying mess with RCTRootViewSizeFlexibility

Summary:
Now things look much more clear, I hope.
This diff:
 * Introduces new property of `RCTRootShadowView` `availableSize` which represents exactly what we transmit to layout engine;
 * Illuminates conflict between logical `availableSize` and explicitly specified size of DOM node (current `size`);
 * Splits overcomplicated `setSize:forView:` method into two unrelated ones;
 * Changes actual values of `RCTRootViewSizeFlexibility` enum constants for simpler usage;
 * Completely removes `sizeFlexibility` concept from `RCTRootShadowView` (in favor of special values of `availableSize`);
 * Makes the code clearer finally.

This is beginning of big effort to improve `RCTRootView` and co.

Reviewed By: mmmulani

Differential Revision: D4562834

fbshipit-source-id: f5baaf2859ea430d44645a6b5d35f222f15a668e
This commit is contained in:
Valentin Shergin 2017-02-19 23:05:42 -08:00 коммит произвёл Facebook Github Bot
Родитель 4a893ee071
Коммит ba170ec78c
6 изменённых файлов: 95 добавлений и 64 удалений

Просмотреть файл

@ -22,9 +22,9 @@
*/ */
typedef NS_ENUM(NSInteger, RCTRootViewSizeFlexibility) { typedef NS_ENUM(NSInteger, RCTRootViewSizeFlexibility) {
RCTRootViewSizeFlexibilityNone = 0, RCTRootViewSizeFlexibilityNone = 0,
RCTRootViewSizeFlexibilityWidth, RCTRootViewSizeFlexibilityWidth = 1 << 0,
RCTRootViewSizeFlexibilityHeight, RCTRootViewSizeFlexibilityHeight = 1 << 1,
RCTRootViewSizeFlexibilityWidthAndHeight, RCTRootViewSizeFlexibilityWidthAndHeight = RCTRootViewSizeFlexibilityWidth | RCTRootViewSizeFlexibilityHeight,
}; };
/** /**

Просмотреть файл

@ -45,6 +45,7 @@ NSString *const RCTContentDidAppearNotification = @"RCTContentDidAppearNotificat
@property (nonatomic, readonly) BOOL contentHasAppeared; @property (nonatomic, readonly) BOOL contentHasAppeared;
@property (nonatomic, readonly, strong) RCTTouchHandler *touchHandler; @property (nonatomic, readonly, strong) RCTTouchHandler *touchHandler;
@property (nonatomic, assign) BOOL passThroughTouches; @property (nonatomic, assign) BOOL passThroughTouches;
@property (nonatomic, assign) RCTRootViewSizeFlexibility sizeFlexibility;
- (instancetype)initWithFrame:(CGRect)frame - (instancetype)initWithFrame:(CGRect)frame
bridge:(RCTBridge *)bridge bridge:(RCTBridge *)bridge
@ -281,8 +282,13 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
- (void)setSizeFlexibility:(RCTRootViewSizeFlexibility)sizeFlexibility - (void)setSizeFlexibility:(RCTRootViewSizeFlexibility)sizeFlexibility
{ {
if (_sizeFlexibility == sizeFlexibility) {
return;
}
_sizeFlexibility = sizeFlexibility; _sizeFlexibility = sizeFlexibility;
[self setNeedsLayout]; [self setNeedsLayout];
_contentView.sizeFlexibility = _sizeFlexibility;
} }
- (void)layoutSubviews - (void)layoutSubviews
@ -383,9 +389,10 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
if ((self = [super initWithFrame:frame])) { if ((self = [super initWithFrame:frame])) {
_bridge = bridge; _bridge = bridge;
self.reactTag = reactTag; self.reactTag = reactTag;
_sizeFlexibility = sizeFlexibility;
_touchHandler = [[RCTTouchHandler alloc] initWithBridge:_bridge]; _touchHandler = [[RCTTouchHandler alloc] initWithBridge:_bridge];
[_touchHandler attachToView:self]; [_touchHandler attachToView:self];
[_bridge.uiManager registerRootView:self withSizeFlexibility:sizeFlexibility]; [_bridge.uiManager registerRootView:self];
self.layer.backgroundColor = NULL; self.layer.backgroundColor = NULL;
} }
return self; return self;
@ -394,6 +401,12 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
RCT_NOT_IMPLEMENTED(-(instancetype)initWithFrame:(CGRect)frame) RCT_NOT_IMPLEMENTED(-(instancetype)initWithFrame:(CGRect)frame)
RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder:(nonnull NSCoder *)aDecoder) RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder:(nonnull NSCoder *)aDecoder)
- (void)layoutSubviews
{
[super layoutSubviews];
[self updateAvailableSize];
}
- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex
{ {
[super insertReactSubview:subview atIndex:atIndex]; [super insertReactSubview:subview atIndex:atIndex];
@ -407,12 +420,30 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder:(nonnull NSCoder *)aDecoder)
}); });
} }
- (void)setFrame:(CGRect)frame - (void)setSizeFlexibility:(RCTRootViewSizeFlexibility)sizeFlexibility
{ {
super.frame = frame; if (_sizeFlexibility == sizeFlexibility) {
if (self.reactTag && _bridge.isValid) { return;
[_bridge.uiManager setSize:frame.size forView:self];
} }
_sizeFlexibility = sizeFlexibility;
[self setNeedsLayout];
}
- (void)updateAvailableSize
{
if (!self.reactTag || !_bridge.isValid) {
return;
}
CGSize size = self.bounds.size;
CGSize availableSize =
CGSizeMake(
_sizeFlexibility & RCTRootViewSizeFlexibilityWidth ? INFINITY : size.width,
_sizeFlexibility & RCTRootViewSizeFlexibilityHeight ? INFINITY : size.height
);
[_bridge.uiManager setAvailableSize:availableSize forRootView:self];
} }
- (void)setBackgroundColor:(UIColor *)backgroundColor - (void)setBackgroundColor:(UIColor *)backgroundColor

Просмотреть файл

@ -58,7 +58,7 @@ RCT_EXTERN NSString *const RCTUIManagerRootViewKey;
/** /**
* Register a root view with the RCTUIManager. * Register a root view with the RCTUIManager.
*/ */
- (void)registerRootView:(UIView *)rootView withSizeFlexibility:(RCTRootViewSizeFlexibility)sizeFlexibility; - (void)registerRootView:(UIView *)rootView;
/** /**
* Gets the view name associated with a reactTag. * Gets the view name associated with a reactTag.
@ -70,6 +70,15 @@ RCT_EXTERN NSString *const RCTUIManagerRootViewKey;
*/ */
- (UIView *)viewForReactTag:(NSNumber *)reactTag; - (UIView *)viewForReactTag:(NSNumber *)reactTag;
/**
* Set the available size (`availableSize` property) for a root view.
* This might be used in response to changes in external layout constraints.
* This value will be directly trasmitted to layout engine and defines how big viewport is;
* this value does not affect root node size style properties.
* Can be considered as something similar to `setSize:forView:` but applicable only for root view.
*/
- (void)setAvailableSize:(CGSize)availableSize forRootView:(UIView *)rootView;
/** /**
* Set the size of a view. This might be in response to a screen rotation * Set the size of a view. This might be in response to a screen rotation
* or some other layout event outside of the React-managed view hierarchy. * or some other layout event outside of the React-managed view hierarchy.
@ -149,6 +158,14 @@ RCT_EXTERN NSString *const RCTUIManagerRootViewKey;
- (void)setFrame:(CGRect)frame forView:(UIView *)view - (void)setFrame:(CGRect)frame forView:(UIView *)view
__deprecated_msg("Use `setSize:forView:` or `setIntrinsicContentSize:forView:` instead."); __deprecated_msg("Use `setSize:forView:` or `setIntrinsicContentSize:forView:` instead.");
/**
* This method is deprecated and will be removed in next releases.
* Use `registerRootView:` instead. There is no need to specify `sizeFlexibility` anymore.
*/
- (void)registerRootView:(UIView *)rootView withSizeFlexibility:(RCTRootViewSizeFlexibility)sizeFlexibility
__deprecated_msg("Use `registerRootView:` instead.");
@end @end
/** /**

Просмотреть файл

@ -376,7 +376,7 @@ dispatch_queue_t RCTGetUIManagerQueue(void)
return RCTGetUIManagerQueue(); return RCTGetUIManagerQueue();
} }
- (void)registerRootView:(UIView *)rootView withSizeFlexibility:(RCTRootViewSizeFlexibility)sizeFlexibility - (void)registerRootView:(UIView *)rootView
{ {
RCTAssertMainQueue(); RCTAssertMainQueue();
@ -390,7 +390,6 @@ dispatch_queue_t RCTGetUIManagerQueue(void)
// Register view // Register view
_viewRegistry[reactTag] = rootView; _viewRegistry[reactTag] = rootView;
CGSize size = rootView.bounds.size;
// Register shadow view // Register shadow view
dispatch_async(RCTGetUIManagerQueue(), ^{ dispatch_async(RCTGetUIManagerQueue(), ^{
@ -400,10 +399,8 @@ dispatch_queue_t RCTGetUIManagerQueue(void)
RCTRootShadowView *shadowView = [RCTRootShadowView new]; RCTRootShadowView *shadowView = [RCTRootShadowView new];
shadowView.reactTag = reactTag; shadowView.reactTag = reactTag;
shadowView.size = size;
shadowView.backgroundColor = rootView.backgroundColor; shadowView.backgroundColor = rootView.backgroundColor;
shadowView.viewName = NSStringFromClass([rootView class]); shadowView.viewName = NSStringFromClass([rootView class]);
shadowView.sizeFlexibility = sizeFlexibility;
self->_shadowViewRegistry[shadowView.reactTag] = shadowView; self->_shadowViewRegistry[shadowView.reactTag] = shadowView;
[self->_rootViewTags addObject:reactTag]; [self->_rootViewTags addObject:reactTag];
}); });
@ -425,44 +422,39 @@ dispatch_queue_t RCTGetUIManagerQueue(void)
return _viewRegistry[reactTag]; return _viewRegistry[reactTag];
} }
- (void)setAvailableSize:(CGSize)availableSize forRootView:(UIView *)rootView
{
RCTAssertMainQueue();
NSNumber *reactTag = rootView.reactTag;
dispatch_async(RCTGetUIManagerQueue(), ^{
RCTRootShadowView *shadowView = (RCTRootShadowView *)self->_shadowViewRegistry[reactTag];
RCTAssert(shadowView != nil, @"Could not locate shadow view with tag #%@", reactTag);
RCTAssert([shadowView isKindOfClass:[RCTRootShadowView class]], @"Located shadow view (with tag #%@) is actually not root view.", reactTag);
if (CGSizeEqualToSize(availableSize, shadowView.availableSize)) {
return;
}
shadowView.availableSize = availableSize;
[self setNeedsLayout];
});
}
- (void)setSize:(CGSize)size forView:(UIView *)view - (void)setSize:(CGSize)size forView:(UIView *)view
{ {
RCTAssertMainQueue(); RCTAssertMainQueue();
// The following variable has no meaning if the view is not a react root view
RCTRootViewSizeFlexibility sizeFlexibility = RCTRootViewSizeFlexibilityNone;
if (RCTIsReactRootView(view.reactTag)) {
RCTRootView *rootView = (RCTRootView *)[view superview];
if (rootView != nil) {
sizeFlexibility = rootView.sizeFlexibility;
}
}
NSNumber *reactTag = view.reactTag; NSNumber *reactTag = view.reactTag;
dispatch_async(RCTGetUIManagerQueue(), ^{ dispatch_async(RCTGetUIManagerQueue(), ^{
RCTShadowView *shadowView = self->_shadowViewRegistry[reactTag]; RCTShadowView *shadowView = self->_shadowViewRegistry[reactTag];
RCTAssert(shadowView != nil, @"Could not locate shadow view with tag #%@", reactTag); RCTAssert(shadowView != nil, @"Could not locate shadow view with tag #%@", reactTag);
BOOL needsLayout = NO; if (CGSizeEqualToSize(size, shadowView.size)) {
if (!CGSizeEqualToSize(size, shadowView.size)) { return;
}
shadowView.size = size; shadowView.size = size;
needsLayout = YES;
}
// Trigger re-layout when size flexibility changes, as the root view might grow or
// shrink in the flexible dimensions.
if (RCTIsReactRootView(reactTag)) {
RCTRootShadowView *rootShadowView = (RCTRootShadowView *)shadowView;
if (rootShadowView.sizeFlexibility != sizeFlexibility) {
rootShadowView.sizeFlexibility = sizeFlexibility;
needsLayout = YES;
}
}
if (needsLayout) {
[self setNeedsLayout]; [self setNeedsLayout];
}
}); });
} }
@ -1652,6 +1644,12 @@ static UIView *_jsResponder;
@implementation RCTUIManager (Deprecated) @implementation RCTUIManager (Deprecated)
- (void)registerRootView:(UIView *)rootView withSizeFlexibility:(RCTRootViewSizeFlexibility)sizeFlexibility
{
RCTLogWarn(@"Calling of `[-RCTUIManager registerRootView:withSizeFlexibility:]` which is deprecated.");
[self registerRootView:rootView];
}
- (void)setFrame:(CGRect)frame forView:(UIView *)view - (void)setFrame:(CGRect)frame forView:(UIView *)view
{ {
RCTLogWarn(@"Calling of `[-RCTUIManager setFrame:forView:]` which is deprecated."); RCTLogWarn(@"Calling of `[-RCTUIManager setFrame:forView:]` which is deprecated.");

Просмотреть файл

@ -13,10 +13,10 @@
@interface RCTRootShadowView : RCTShadowView @interface RCTRootShadowView : RCTShadowView
/** /**
* Size flexibility type used to find size constraints. * Available size to layout all views.
* Default to RCTRootViewSizeFlexibilityNone * Defaults to {INFINITY, INFINITY}
*/ */
@property (nonatomic, assign) RCTRootViewSizeFlexibility sizeFlexibility; @property (nonatomic, assign) CGSize availableSize;
/** /**
* Layout direction (LTR or RTL) inherited from native environment and * Layout direction (LTR or RTL) inherited from native environment and

Просмотреть файл

@ -22,33 +22,18 @@
self = [super init]; self = [super init];
if (self) { if (self) {
_baseDirection = [[RCTI18nUtil sharedInstance] isRTL] ? YGDirectionRTL : YGDirectionLTR; _baseDirection = [[RCTI18nUtil sharedInstance] isRTL] ? YGDirectionRTL : YGDirectionLTR;
_availableSize = CGSizeMake(INFINITY, INFINITY);
} }
return self; return self;
} }
- (void)applySizeConstraints
{
switch (_sizeFlexibility) {
case RCTRootViewSizeFlexibilityNone:
break;
case RCTRootViewSizeFlexibilityWidth:
YGNodeStyleSetWidth(self.cssNode, YGUndefined);
break;
case RCTRootViewSizeFlexibilityHeight:
YGNodeStyleSetHeight(self.cssNode, YGUndefined);
break;
case RCTRootViewSizeFlexibilityWidthAndHeight:
YGNodeStyleSetWidth(self.cssNode, YGUndefined);
YGNodeStyleSetHeight(self.cssNode, YGUndefined);
break;
}
}
- (NSSet<RCTShadowView *> *)collectViewsWithUpdatedFrames - (NSSet<RCTShadowView *> *)collectViewsWithUpdatedFrames
{ {
[self applySizeConstraints]; // Treating `INFINITY` as `YGUndefined` (which equals `NAN`).
float availableWidth = _availableSize.width == INFINITY ? YGUndefined : _availableSize.width;
float availableHeight = _availableSize.height == INFINITY ? YGUndefined : _availableSize.height;
YGNodeCalculateLayout(self.cssNode, YGUndefined, YGUndefined, _baseDirection); YGNodeCalculateLayout(self.cssNode, availableWidth, availableHeight, _baseDirection);
NSMutableSet<RCTShadowView *> *viewsWithNewFrame = [NSMutableSet set]; NSMutableSet<RCTShadowView *> *viewsWithNewFrame = [NSMutableSet set];
[self applyLayoutNode:self.cssNode viewsWithNewFrame:viewsWithNewFrame absolutePosition:CGPointZero]; [self applyLayoutNode:self.cssNode viewsWithNewFrame:viewsWithNewFrame absolutePosition:CGPointZero];