This commit is contained in:
Yamozha
2021-04-02 02:24:13 +03:00
parent c23950b545
commit 7256d79e2c
31493 changed files with 3036630 additions and 0 deletions

View File

@ -0,0 +1,141 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
#import <React/RCTSurfaceStage.h>
NS_ASSUME_NONNULL_BEGIN
@class RCTBridge;
@class RCTSurfaceView;
@protocol RCTSurfaceDelegate;
/**
* RCTSurface instance represents React Native-powered piece of a user interface
* which can be a full-screen app, separate modal view controller,
* or even small widget.
* It is called "Surface".
*
* The RCTSurface instance is completely thread-safe by design;
* it can be created on any thread, and any its method can be called from
* any thread (if the opposite is not mentioned explicitly).
*
* The primary goals of the RCTSurface are:
* * ability to measure and layout the surface in a thread-safe
* and synchronous manner;
* * ability to create a UIView instance on demand (later);
* * ability to communicate the current stage of the surface granularly.
*/
@interface RCTSurface : NSObject
@property (atomic, readonly) RCTSurfaceStage stage;
@property (atomic, readonly) NSString *moduleName;
@property (atomic, readonly) NSNumber *rootViewTag;
@property (atomic, readwrite, weak, nullable) id<RCTSurfaceDelegate> delegate;
@property (atomic, copy, readwrite) NSDictionary *properties;
- (instancetype)initWithBridge:(RCTBridge *)bridge
moduleName:(NSString *)moduleName
initialProperties:(NSDictionary *)initialProperties;
#pragma mark - Dealing with UIView representation, the Main thread only access
/**
* Creates (if needed) and returns `UIView` instance which represents the Surface.
* The Surface will cache and *retain* this object.
* Returning the UIView instance does not mean that the Surface is ready
* to execute and layout. It can be just a handler which Surface will use later
* to mount the actual views.
* RCTSurface does not control (or influence in any way) the size or origin
* of this view. Some superview (or another owner) must use other methods
* of this class to setup proper layout and interop interactions with UIKit
* or another UI framework.
* This method must be called only from the main queue.
*/
- (RCTSurfaceView *)view;
#pragma mark - Layout: Setting the size constrains
/**
* Sets `minimumSize` and `maximumSize` layout constraints for the Surface.
*/
- (void)setMinimumSize:(CGSize)minimumSize maximumSize:(CGSize)maximumSize;
/**
* Previously set `minimumSize` layout constraint.
* Defaults to `{0, 0}`.
*/
@property (atomic, assign, readonly) CGSize minimumSize;
/**
* Previously set `maximumSize` layout constraint.
* Defaults to `{CGFLOAT_MAX, CGFLOAT_MAX}`.
*/
@property (atomic, assign, readonly) CGSize maximumSize;
/**
* Simple shortcut to `-[RCTSurface setMinimumSize:size maximumSize:size]`.
*/
- (void)setSize:(CGSize)size;
#pragma mark - Layout: Measuring
/**
* Measures the Surface with given constraints.
* This method does not cause any side effects on the surface object.
*/
- (CGSize)sizeThatFitsMinimumSize:(CGSize)minimumSize maximumSize:(CGSize)maximumSize;
/**
* Return the current size of the root view based on (but not clamp by) current
* size constraints.
*/
@property (atomic, assign, readonly) CGSize intrinsicSize;
#pragma mark - Synchronous waiting
/**
* Synchronously blocks the current thread up to given `timeout` until
* the Surface reaches `stage`.
* Limitations:
* - Do nothing, if called on `UIManager` queue.
* - Calling on the main queue with `RCTSurfaceStageSurfaceDidInitialMounting`
* stage temporary is not supported; in this case the stage will be
* downgraded to `RCTSurfaceStageSurfaceDidInitialLayout`.
*/
- (BOOL)synchronouslyWaitForStage:(RCTSurfaceStage)stage timeout:(NSTimeInterval)timeout;
#pragma mark - Start & Stop
/**
* Starts or stops the Surface.
* Those methods are a no-op for regular RCTSurface (for now), but all call sites must call them appropriately.
*/
- (BOOL)start;
- (BOOL)stop;
#pragma mark - Mounting/Unmounting of React components
/**
* Mount the React component specified by the given moduleName. This is typically
* calling runApplication.js from the native side.
*/
- (void)mountReactComponentWithBridge:(RCTBridge *)bridge
moduleName:(NSString *)moduleName
params:(NSDictionary *)params;
/**
* Unmount the React component specified by the given rootViewTag, called from native.
*/
- (void)unmountReactComponentWithBridge:(RCTBridge *)bridge rootViewTag:(NSNumber *)rootViewTag;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,592 @@
/*
* 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.
*/
#import "RCTSurface.h"
#import "RCTSurfaceView+Internal.h"
#import <mutex>
#import <stdatomic.h>
#import "RCTAssert.h"
#import "RCTBridge+Private.h"
#import "RCTBridge.h"
#import "RCTShadowView+Layout.h"
#import "RCTSurfaceDelegate.h"
#import "RCTSurfaceRootShadowView.h"
#import "RCTSurfaceRootShadowViewDelegate.h"
#import "RCTSurfaceRootView.h"
#import "RCTSurfaceView.h"
#import "RCTTouchHandler.h"
#import "RCTUIManager.h"
#import "RCTUIManagerObserverCoordinator.h"
#import "RCTUIManagerUtils.h"
@interface RCTSurface () <RCTSurfaceRootShadowViewDelegate, RCTUIManagerObserver>
@end
@implementation RCTSurface {
// Immutable
RCTBridge *_bridge;
NSString *_moduleName;
NSNumber *_rootViewTag;
// Protected by the `_mutex`
std::mutex _mutex;
RCTBridge *_batchedBridge;
RCTSurfaceStage _stage;
NSDictionary *_properties;
CGSize _minimumSize;
CGSize _maximumSize;
CGSize _intrinsicSize;
RCTUIManagerMountingBlock _mountingBlock;
// The Main thread only
RCTSurfaceView *_Nullable _view;
RCTTouchHandler *_Nullable _touchHandler;
// Semaphores
dispatch_semaphore_t _rootShadowViewDidStartRenderingSemaphore;
dispatch_semaphore_t _rootShadowViewDidStartLayingOutSemaphore;
dispatch_semaphore_t _uiManagerDidPerformMountingSemaphore;
// Atomics
atomic_bool _waitingForMountingStageOnMainQueue;
}
- (instancetype)initWithBridge:(RCTBridge *)bridge
moduleName:(NSString *)moduleName
initialProperties:(NSDictionary *)initialProperties
{
RCTAssert(bridge.valid, @"Valid bridge is required to instantiate `RCTSurface`.");
if (self = [super init]) {
_bridge = bridge;
_batchedBridge = [_bridge batchedBridge] ?: _bridge;
_moduleName = moduleName;
_properties = [initialProperties copy];
_rootViewTag = RCTAllocateRootViewTag();
_rootShadowViewDidStartRenderingSemaphore = dispatch_semaphore_create(0);
_rootShadowViewDidStartLayingOutSemaphore = dispatch_semaphore_create(0);
_uiManagerDidPerformMountingSemaphore = dispatch_semaphore_create(0);
_minimumSize = CGSizeZero;
_maximumSize = CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX);
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleBridgeWillLoadJavaScriptNotification:)
name:RCTJavaScriptWillStartLoadingNotification
object:_bridge];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleBridgeDidLoadJavaScriptNotification:)
name:RCTJavaScriptDidLoadNotification
object:_bridge];
_stage = RCTSurfaceStageSurfaceDidInitialize;
if (!bridge.loading) {
_stage = _stage | RCTSurfaceStageBridgeDidLoad;
}
[_bridge.uiManager.observerCoordinator addObserver:self];
[self _registerRootView];
[self _run];
}
return self;
}
- (void)dealloc
{
[self _stop];
}
#pragma mark - Immutable Properties (no need to enforce synchronization)
- (RCTBridge *)bridge
{
return _bridge;
}
- (NSString *)moduleName
{
return _moduleName;
}
- (NSNumber *)rootViewTag
{
return _rootViewTag;
}
#pragma mark - Convenience Internal Thread-Safe Properties
- (RCTBridge *)_batchedBridge
{
std::lock_guard<std::mutex> lock(_mutex);
return _batchedBridge;
}
- (RCTUIManager *)_uiManager
{
return self._batchedBridge.uiManager;
}
#pragma mark - Main-Threaded Routines
- (RCTSurfaceView *)view
{
RCTAssertMainQueue();
if (!_view) {
_view = [[RCTSurfaceView alloc] initWithSurface:self];
_touchHandler = [[RCTTouchHandler alloc] initWithBridge:self.bridge];
[_touchHandler attachToView:_view];
[self _mountRootViewIfNeeded];
}
return _view;
}
- (void)_mountRootViewIfNeeded
{
RCTAssertMainQueue();
RCTSurfaceView *view = self->_view;
if (!view) {
return;
}
RCTSurfaceRootView *rootView = (RCTSurfaceRootView *)[self._uiManager viewForReactTag:self->_rootViewTag];
if (!rootView) {
return;
}
RCTAssert(
[rootView isKindOfClass:[RCTSurfaceRootView class]],
@"Received root view is not an instance of `RCTSurfaceRootView`.");
if (rootView.superview != view) {
view.rootView = rootView;
}
}
#pragma mark - Bridge Events
- (void)handleBridgeWillLoadJavaScriptNotification:(__unused NSNotification *)notification
{
RCTAssertMainQueue();
// Reset states because the bridge is reloading. This is similar to initialization phase.
_stage = RCTSurfaceStageSurfaceDidInitialize;
_view = nil;
_touchHandler = nil;
[self _setStage:RCTSurfaceStageBridgeDidLoad];
}
- (void)handleBridgeDidLoadJavaScriptNotification:(NSNotification *)notification
{
RCTAssertMainQueue();
[self _setStage:RCTSurfaceStageModuleDidLoad];
RCTBridge *bridge = notification.userInfo[@"bridge"];
BOOL isRerunNeeded = NO;
{
std::lock_guard<std::mutex> lock(_mutex);
if (bridge != _batchedBridge) {
_batchedBridge = bridge;
isRerunNeeded = YES;
}
}
if (isRerunNeeded) {
[self _registerRootView];
[self _run];
}
}
#pragma mark - Stage management
- (RCTSurfaceStage)stage
{
std::lock_guard<std::mutex> lock(_mutex);
return _stage;
}
- (void)_setStage:(RCTSurfaceStage)stage
{
RCTSurfaceStage updatedStage;
{
std::lock_guard<std::mutex> lock(_mutex);
if (_stage & stage) {
return;
}
updatedStage = (RCTSurfaceStage)(_stage | stage);
_stage = updatedStage;
}
[self _propagateStageChange:updatedStage];
}
- (void)_propagateStageChange:(RCTSurfaceStage)stage
{
// Updating the `view`
RCTExecuteOnMainQueue(^{
self->_view.stage = stage;
});
// Notifying the `delegate`
id<RCTSurfaceDelegate> delegate = self.delegate;
if ([delegate respondsToSelector:@selector(surface:didChangeStage:)]) {
[delegate surface:self didChangeStage:stage];
}
}
#pragma mark - Properties Management
- (NSDictionary *)properties
{
std::lock_guard<std::mutex> lock(_mutex);
return _properties;
}
- (void)setProperties:(NSDictionary *)properties
{
{
std::lock_guard<std::mutex> lock(_mutex);
if ([properties isEqualToDictionary:_properties]) {
return;
}
_properties = [properties copy];
}
[self _run];
}
#pragma mark - Running
- (void)_run
{
RCTBridge *batchedBridge;
NSDictionary *properties;
{
std::lock_guard<std::mutex> lock(_mutex);
batchedBridge = _batchedBridge;
properties = _properties;
}
if (!batchedBridge.valid) {
return;
}
NSDictionary *applicationParameters = @{
@"rootTag" : _rootViewTag,
@"initialProps" : properties,
};
RCTLogInfo(@"Running surface %@ (%@)", _moduleName, applicationParameters);
[self mountReactComponentWithBridge:batchedBridge moduleName:_moduleName params:applicationParameters];
[self _setStage:RCTSurfaceStageSurfaceDidRun];
}
- (void)_stop
{
[self unmountReactComponentWithBridge:self._batchedBridge rootViewTag:self->_rootViewTag];
}
- (void)_registerRootView
{
RCTBridge *batchedBridge;
CGSize minimumSize;
CGSize maximumSize;
{
std::lock_guard<std::mutex> lock(_mutex);
batchedBridge = _batchedBridge;
minimumSize = _minimumSize;
maximumSize = _maximumSize;
}
RCTUIManager *uiManager = batchedBridge.uiManager;
// If we are on the main queue now, we have to proceed synchronously.
// Otherwise, we cannot perform synchronous waiting for some stages later.
(RCTIsMainQueue() ? RCTUnsafeExecuteOnUIManagerQueueSync : RCTExecuteOnUIManagerQueue)(^{
[uiManager registerRootViewTag:self->_rootViewTag];
RCTSurfaceRootShadowView *rootShadowView =
(RCTSurfaceRootShadowView *)[uiManager shadowViewForReactTag:self->_rootViewTag];
RCTAssert(
[rootShadowView isKindOfClass:[RCTSurfaceRootShadowView class]],
@"Received shadow view is not an instance of `RCTSurfaceRootShadowView`.");
[rootShadowView setMinimumSize:minimumSize maximumSize:maximumSize];
rootShadowView.delegate = self;
});
}
#pragma mark - Layout
- (CGSize)sizeThatFitsMinimumSize:(CGSize)minimumSize maximumSize:(CGSize)maximumSize
{
RCTUIManager *uiManager = self._uiManager;
__block CGSize fittingSize;
RCTUnsafeExecuteOnUIManagerQueueSync(^{
RCTSurfaceRootShadowView *rootShadowView =
(RCTSurfaceRootShadowView *)[uiManager shadowViewForReactTag:self->_rootViewTag];
RCTAssert(
[rootShadowView isKindOfClass:[RCTSurfaceRootShadowView class]],
@"Received shadow view is not an instance of `RCTSurfaceRootShadowView`.");
fittingSize = [rootShadowView sizeThatFitsMinimumSize:minimumSize maximumSize:maximumSize];
});
return fittingSize;
}
#pragma mark - Size Constraints
- (void)setSize:(CGSize)size
{
[self setMinimumSize:size maximumSize:size];
}
- (void)setMinimumSize:(CGSize)minimumSize maximumSize:(CGSize)maximumSize
{
{
std::lock_guard<std::mutex> lock(_mutex);
if (CGSizeEqualToSize(minimumSize, _minimumSize) && CGSizeEqualToSize(maximumSize, _maximumSize)) {
return;
}
_maximumSize = maximumSize;
_minimumSize = minimumSize;
}
RCTUIManager *uiManager = self._uiManager;
RCTUnsafeExecuteOnUIManagerQueueSync(^{
RCTSurfaceRootShadowView *rootShadowView =
(RCTSurfaceRootShadowView *)[uiManager shadowViewForReactTag:self->_rootViewTag];
RCTAssert(
[rootShadowView isKindOfClass:[RCTSurfaceRootShadowView class]],
@"Received shadow view is not an instance of `RCTSurfaceRootShadowView`.");
[rootShadowView setMinimumSize:minimumSize maximumSize:maximumSize];
[uiManager setNeedsLayout];
});
}
- (CGSize)minimumSize
{
std::lock_guard<std::mutex> lock(_mutex);
return _minimumSize;
}
- (CGSize)maximumSize
{
std::lock_guard<std::mutex> lock(_mutex);
return _maximumSize;
}
#pragma mark - intrinsicSize
- (void)setIntrinsicSize:(CGSize)intrinsicSize
{
{
std::lock_guard<std::mutex> lock(_mutex);
if (CGSizeEqualToSize(intrinsicSize, _intrinsicSize)) {
return;
}
_intrinsicSize = intrinsicSize;
}
// Notifying `delegate`
id<RCTSurfaceDelegate> delegate = self.delegate;
if ([delegate respondsToSelector:@selector(surface:didChangeIntrinsicSize:)]) {
[delegate surface:self didChangeIntrinsicSize:intrinsicSize];
}
}
- (CGSize)intrinsicSize
{
std::lock_guard<std::mutex> lock(_mutex);
return _intrinsicSize;
}
#pragma mark - Synchronous Waiting
- (BOOL)synchronouslyWaitForStage:(RCTSurfaceStage)stage timeout:(NSTimeInterval)timeout
{
if (RCTIsUIManagerQueue()) {
RCTLogInfo(@"Synchronous waiting is not supported on UIManager queue.");
return NO;
}
if (RCTIsMainQueue() && (stage & RCTSurfaceStageSurfaceDidInitialMounting)) {
// All main-threaded execution (especially mounting process) has to be
// intercepted, captured and performed synchronously at the end of this method
// right after the semaphore signals.
// Atomic variant of `_waitingForMountingStageOnMainQueue = YES;`
atomic_fetch_or(&_waitingForMountingStageOnMainQueue, 1);
}
dispatch_semaphore_t semaphore;
switch (stage) {
case RCTSurfaceStageSurfaceDidInitialLayout:
semaphore = _rootShadowViewDidStartLayingOutSemaphore;
break;
case RCTSurfaceStageSurfaceDidInitialRendering:
semaphore = _rootShadowViewDidStartRenderingSemaphore;
break;
case RCTSurfaceStageSurfaceDidInitialMounting:
semaphore = _uiManagerDidPerformMountingSemaphore;
break;
default:
RCTAssert(
NO,
@"Only waiting for `RCTSurfaceStageSurfaceDidInitialRendering`, `RCTSurfaceStageSurfaceDidInitialLayout` and `RCTSurfaceStageSurfaceDidInitialMounting` stages are supported.");
}
BOOL timeoutOccurred = dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, timeout * NSEC_PER_SEC));
// Atomic equivalent of `_waitingForMountingStageOnMainQueue = NO;`.
atomic_fetch_and(&_waitingForMountingStageOnMainQueue, 0);
if (!timeoutOccurred) {
// Balancing the semaphore.
// Note: `dispatch_semaphore_wait` reverts the decrement in case when timeout occurred.
dispatch_semaphore_signal(semaphore);
}
if (RCTIsMainQueue() && (stage & RCTSurfaceStageSurfaceDidInitialMounting)) {
// Time to apply captured mounting block.
RCTUIManagerMountingBlock mountingBlock;
{
std::lock_guard<std::mutex> lock(_mutex);
mountingBlock = _mountingBlock;
_mountingBlock = nil;
}
if (mountingBlock) {
mountingBlock();
[self _mountRootViewIfNeeded];
}
}
return !timeoutOccurred;
}
#pragma mark - RCTSurfaceRootShadowViewDelegate
- (void)rootShadowView:(__unused RCTRootShadowView *)rootShadowView didChangeIntrinsicSize:(CGSize)intrinsicSize
{
self.intrinsicSize = intrinsicSize;
}
- (void)rootShadowViewDidStartRendering:(__unused RCTSurfaceRootShadowView *)rootShadowView
{
[self _setStage:RCTSurfaceStageSurfaceDidInitialRendering];
dispatch_semaphore_signal(_rootShadowViewDidStartRenderingSemaphore);
}
- (void)rootShadowViewDidStartLayingOut:(__unused RCTSurfaceRootShadowView *)rootShadowView
{
[self _setStage:RCTSurfaceStageSurfaceDidInitialLayout];
dispatch_semaphore_signal(_rootShadowViewDidStartLayingOutSemaphore);
RCTExecuteOnMainQueue(^{
// Rendering is happening, let's mount `rootView` into `view` if we already didn't do this.
[self _mountRootViewIfNeeded];
});
}
#pragma mark - RCTUIManagerObserver
- (BOOL)uiManager:(__unused RCTUIManager *)manager performMountingWithBlock:(RCTUIManagerMountingBlock)block
{
if (atomic_load(&_waitingForMountingStageOnMainQueue) && (self.stage & RCTSurfaceStageSurfaceDidInitialLayout)) {
// Atomic equivalent of `_waitingForMountingStageOnMainQueue = NO;`.
atomic_fetch_and(&_waitingForMountingStageOnMainQueue, 0);
{
std::lock_guard<std::mutex> lock(_mutex);
_mountingBlock = block;
}
return YES;
}
return NO;
}
- (void)uiManagerDidPerformMounting:(__unused RCTUIManager *)manager
{
if (self.stage & RCTSurfaceStageSurfaceDidInitialLayout) {
[self _setStage:RCTSurfaceStageSurfaceDidInitialMounting];
dispatch_semaphore_signal(_uiManagerDidPerformMountingSemaphore);
// No need to listen to UIManager anymore.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
[self->_bridge.uiManager.observerCoordinator removeObserver:self];
});
}
}
- (BOOL)start
{
// Does nothing.
// The Start&Stop feature is not implemented for regular Surface yet.
return YES;
}
- (BOOL)stop
{
// Does nothing.
// The Start&Stop feature is not implemented for regular Surface yet.
return YES;
}
#pragma mark - Mounting/Unmounting of React components
- (void)mountReactComponentWithBridge:(RCTBridge *)bridge
moduleName:(NSString *)moduleName
params:(NSDictionary *)params
{
[bridge enqueueJSCall:@"AppRegistry" method:@"runApplication" args:@[ moduleName, params ] completion:NULL];
}
- (void)unmountReactComponentWithBridge:(RCTBridge *)bridge rootViewTag:(NSNumber *)rootViewTag
{
[bridge enqueueJSCall:@"AppRegistry"
method:@"unmountApplicationComponentAtRootTag"
args:@[ rootViewTag ]
completion:NULL];
}
@end

View File

@ -0,0 +1,34 @@
/*
* 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.
*/
#import <UIKit/UIKit.h>
#import <React/RCTSurfaceStage.h>
NS_ASSUME_NONNULL_BEGIN
@class RCTSurface;
@protocol RCTSurfaceDelegate <NSObject>
@optional
/**
* Notifies a receiver that a surface transitioned to a new stage.
* See `RCTSurfaceStage` for more details.
*/
- (void)surface:(RCTSurface *)surface didChangeStage:(RCTSurfaceStage)stage;
/**
* Notifies a receiver that root view got a new (intrinsic) size during the last
* layout pass.
*/
- (void)surface:(RCTSurface *)surface didChangeIntrinsicSize:(CGSize)intrinsicSize;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,32 @@
/*
* 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.
*/
#import <React/RCTShadowView.h>
#import <React/RCTSurfaceRootShadowViewDelegate.h>
#import <yoga/YGEnums.h>
@interface RCTSurfaceRootShadowView : RCTShadowView
@property (nonatomic, assign, readonly) CGSize minimumSize;
@property (nonatomic, assign, readonly) CGSize maximumSize;
- (void)setMinimumSize:(CGSize)size maximumSize:(CGSize)maximumSize;
@property (nonatomic, assign, readonly) CGSize intrinsicSize;
@property (nonatomic, weak) id<RCTSurfaceRootShadowViewDelegate> delegate;
/**
* Layout direction (LTR or RTL) inherited from native environment and
* is using as a base direction value in layout engine.
* Defaults to value inferred from current locale.
*/
@property (nonatomic, assign) YGDirection baseDirection;
- (void)layoutWithAffectedShadowViews:(NSHashTable<RCTShadowView *> *)affectedShadowViews;
@end

View File

@ -0,0 +1,92 @@
/*
* 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.
*/
#import "RCTSurfaceRootShadowView.h"
#import "RCTI18nUtil.h"
#import "RCTShadowView+Layout.h"
#import "RCTUIManagerUtils.h"
@implementation RCTSurfaceRootShadowView {
CGSize _intrinsicSize;
BOOL _isRendered;
BOOL _isLaidOut;
}
- (instancetype)init
{
if (self = [super init]) {
self.viewName = @"RCTSurfaceRootView";
_baseDirection = [[RCTI18nUtil sharedInstance] isRTL] ? YGDirectionRTL : YGDirectionLTR;
_minimumSize = CGSizeZero;
_maximumSize = CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX);
self.alignSelf = YGAlignStretch;
self.flex = 1;
}
return self;
}
- (void)insertReactSubview:(RCTShadowView *)subview atIndex:(NSInteger)atIndex
{
[super insertReactSubview:subview atIndex:atIndex];
if (!_isRendered) {
[_delegate rootShadowViewDidStartRendering:self];
_isRendered = YES;
}
}
- (void)layoutWithAffectedShadowViews:(NSHashTable<RCTShadowView *> *)affectedShadowViews
{
NSHashTable<NSString *> *other = [NSHashTable new];
RCTLayoutContext layoutContext = {};
layoutContext.absolutePosition = CGPointZero;
layoutContext.affectedShadowViews = affectedShadowViews;
layoutContext.other = other;
[self layoutWithMinimumSize:_minimumSize
maximumSize:_maximumSize
layoutDirection:RCTUIKitLayoutDirectionFromYogaLayoutDirection(_baseDirection)
layoutContext:layoutContext];
self.intrinsicSize = self.layoutMetrics.frame.size;
if (_isRendered && !_isLaidOut) {
[_delegate rootShadowViewDidStartLayingOut:self];
_isLaidOut = YES;
}
}
- (void)setMinimumSize:(CGSize)minimumSize maximumSize:(CGSize)maximumSize
{
if (CGSizeEqualToSize(minimumSize, _minimumSize) && CGSizeEqualToSize(maximumSize, _maximumSize)) {
return;
}
_maximumSize = maximumSize;
_minimumSize = minimumSize;
}
- (void)setIntrinsicSize:(CGSize)intrinsicSize
{
if (CGSizeEqualToSize(_intrinsicSize, intrinsicSize)) {
return;
}
_intrinsicSize = intrinsicSize;
[_delegate rootShadowView:self didChangeIntrinsicSize:intrinsicSize];
}
- (CGSize)intrinsicSize
{
return _intrinsicSize;
}
@end

View File

@ -0,0 +1,22 @@
/*
* 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.
*/
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class RCTSurfaceRootShadowView;
@protocol RCTSurfaceRootShadowViewDelegate <NSObject>
- (void)rootShadowView:(RCTSurfaceRootShadowView *)rootShadowView didChangeIntrinsicSize:(CGSize)instrinsicSize;
- (void)rootShadowViewDidStartRendering:(RCTSurfaceRootShadowView *)rootShadowView;
- (void)rootShadowViewDidStartLayingOut:(RCTSurfaceRootShadowView *)rootShadowView;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,21 @@
/*
* 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.
*/
#import <UIKit/UIKit.h>
#import <React/RCTView.h>
NS_ASSUME_NONNULL_BEGIN
/**
* Internal class represents Surface's root view.
*/
@interface RCTSurfaceRootView : RCTView
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,16 @@
/*
* 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.
*/
#import "RCTSurfaceRootView.h"
#import "RCTDefines.h"
@implementation RCTSurfaceRootView
RCT_NOT_IMPLEMENTED(-(nullable instancetype)initWithCoder : (NSCoder *)coder)
@end

View File

@ -0,0 +1,47 @@
/*
* 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.
*/
#import <UIKit/UIKit.h>
#import <React/RCTDefines.h>
/**
* The stage of the Surface
*/
typedef NS_OPTIONS(NSInteger, RCTSurfaceStage) {
RCTSurfaceStageSurfaceDidInitialize = 1 << 0, // Surface object was created
RCTSurfaceStageBridgeDidLoad = 1 << 1, // Bridge was loaded
RCTSurfaceStageModuleDidLoad = 1 << 2, // Module (JavaScript code) was loaded
RCTSurfaceStageSurfaceDidRun = 1 << 3, // Module (JavaScript code) was run
RCTSurfaceStageSurfaceDidInitialRendering = 1 << 4, // UIManager created the first shadow views
RCTSurfaceStageSurfaceDidInitialLayout = 1 << 5, // UIManager completed the first layout pass
RCTSurfaceStageSurfaceDidInitialMounting = 1 << 6, // UIManager completed the first mounting pass
RCTSurfaceStageSurfaceDidStop = 1 << 7, // Surface stopped
// Most of the previously existed stages make no sense in the new architecture;
// now Surface exposes only three simple stages:
//
// Surface object was constructed and still valid.
RCTSurfaceStageInitialized = RCTSurfaceStageSurfaceDidInitialize,
// Surface was started.
RCTSurfaceStageStarted = 1 << 8,
// All off-main-thread work is done; we are ready to mount the UI.
RCTSurfaceStagePrepared = RCTSurfaceStageBridgeDidLoad | RCTSurfaceStageModuleDidLoad | RCTSurfaceStageSurfaceDidRun |
RCTSurfaceStageSurfaceDidInitialRendering | RCTSurfaceStageSurfaceDidInitialLayout,
// All main-thread work is done, the UI was mounted.
RCTSurfaceStageMounted = RCTSurfaceStageSurfaceDidInitialMounting,
};
/**
* Returns `YES` if the stage is suitable for displaying normal React Native app.
*/
RCT_EXTERN BOOL RCTSurfaceStageIsRunning(RCTSurfaceStage stage);
/**
* Returns `YES` if the stage is suitable for displaying activity indicator.
*/
RCT_EXTERN BOOL RCTSurfaceStageIsPreparing(RCTSurfaceStage stage);

View File

@ -0,0 +1,18 @@
/*
* 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.
*/
#import "RCTSurfaceStage.h"
BOOL RCTSurfaceStageIsRunning(RCTSurfaceStage stage)
{
return (stage & RCTSurfaceStageSurfaceDidInitialLayout) && !(stage & RCTSurfaceStageSurfaceDidStop);
}
BOOL RCTSurfaceStageIsPreparing(RCTSurfaceStage stage)
{
return !(stage & RCTSurfaceStageSurfaceDidInitialLayout) && !(stage & RCTSurfaceStageSurfaceDidStop);
}

View File

@ -0,0 +1,24 @@
/*
* 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.
*/
#import <UIKit/UIKit.h>
#import <React/RCTSurfaceStage.h>
#import <React/RCTSurfaceView.h>
@class RCTSurfaceRootView;
NS_ASSUME_NONNULL_BEGIN
@interface RCTSurfaceView (Internal)
@property (nonatomic, strong) RCTSurfaceRootView *rootView;
@property (nonatomic, assign) RCTSurfaceStage stage;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,25 @@
/*
* 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.
*/
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class RCTSurface;
/**
* UIView instance which represents the Surface
*/
@interface RCTSurfaceView : UIView
- (instancetype)initWithSurface:(RCTSurface *)surface NS_DESIGNATED_INITIALIZER;
@property (nonatomic, weak, readonly, nullable) RCTSurface *surface;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,83 @@
/*
* 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.
*/
#import "RCTSurfaceView.h"
#import "RCTSurfaceView+Internal.h"
#import "RCTDefines.h"
#import "RCTSurface.h"
#import "RCTSurfaceRootView.h"
@implementation RCTSurfaceView {
RCTSurfaceRootView *_Nullable _rootView;
RCTSurfaceStage _stage;
}
RCT_NOT_IMPLEMENTED(-(instancetype)init)
RCT_NOT_IMPLEMENTED(-(instancetype)initWithFrame : (CGRect)frame)
RCT_NOT_IMPLEMENTED(-(nullable instancetype)initWithCoder : (NSCoder *)coder)
- (instancetype)initWithSurface:(RCTSurface *)surface
{
if (self = [super initWithFrame:CGRectZero]) {
_stage = surface.stage;
_surface = surface;
}
return self;
}
#pragma mark - Internal Interface
- (void)setRootView:(RCTSurfaceRootView *)rootView
{
if (_rootView == rootView) {
return;
}
[_rootView removeFromSuperview];
_rootView = rootView;
[self _updateStage];
}
- (RCTSurfaceRootView *)rootView
{
return _rootView;
}
#pragma mark - stage
- (void)setStage:(RCTSurfaceStage)stage
{
if (stage == _stage) {
return;
}
_stage = stage;
[self _updateStage];
}
- (RCTSurfaceStage)stage
{
return _stage;
}
#pragma mark - Private
- (void)_updateStage
{
if (RCTSurfaceStageIsRunning(_stage)) {
if (_rootView.superview != self) {
[self addSubview:_rootView];
}
} else {
[_rootView removeFromSuperview];
}
}
@end

View File

@ -0,0 +1,55 @@
/*
* 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.
*/
#import <UIKit/UIKit.h>
#import <React/RCTRootView.h>
#import "RCTSurfaceHostingView.h"
NS_ASSUME_NONNULL_BEGIN
/**
* This is a RCTRootView-compatible implementation of RCTSurfaceHostingView.
* Use this class to replace all usages of RCTRootView in the app for easier migration
* to RCTSurfaceHostingView.
*
* WARNING: In the future, RCTRootView will be deprecated in favor of RCTSurfaceHostingView.
*/
@interface RCTSurfaceHostingProxyRootView : RCTSurfaceHostingView
#pragma mark RCTRootView compatibility - keep these sync'ed with RCTRootView.h
@property (nonatomic, copy, readonly) NSString *moduleName;
@property (nonatomic, strong, readonly) RCTBridge *bridge;
@property (nonatomic, copy, readwrite) NSDictionary *appProperties;
@property (nonatomic, assign) RCTRootViewSizeFlexibility sizeFlexibility;
@property (nonatomic, weak) id<RCTRootViewDelegate> delegate;
@property (nonatomic, weak) UIViewController *reactViewController;
@property (nonatomic, strong, readonly) UIView *contentView;
@property (nonatomic, strong) UIView *loadingView;
@property (nonatomic, assign) BOOL passThroughTouches;
@property (nonatomic, assign) NSTimeInterval loadingViewFadeDelay;
@property (nonatomic, assign) NSTimeInterval loadingViewFadeDuration;
- (instancetype)initWithBridge:(RCTBridge *)bridge
moduleName:(NSString *)moduleName
initialProperties:(NSDictionary *)initialProperties NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithBundleURL:(NSURL *)bundleURL
moduleName:(NSString *)moduleName
initialProperties:(NSDictionary *)initialProperties
launchOptions:(NSDictionary *)launchOptions;
- (instancetype)initWithSurface:(RCTSurface *)surface
sizeMeasureMode:(RCTSurfaceSizeMeasureMode)sizeMeasureMode NS_UNAVAILABLE;
- (void)cancelTouches;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,182 @@
/*
* 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.
*/
#import "RCTSurfaceHostingProxyRootView.h"
#import <objc/runtime.h>
#import "RCTAssert.h"
#import "RCTBridge.h"
#import "RCTLog.h"
#import "RCTPerformanceLogger.h"
#import "RCTProfile.h"
#import "RCTRootContentView.h"
#import "RCTRootViewDelegate.h"
#import "RCTSurface.h"
#import "UIView+React.h"
static RCTSurfaceSizeMeasureMode convertToSurfaceSizeMeasureMode(RCTRootViewSizeFlexibility sizeFlexibility)
{
switch (sizeFlexibility) {
case RCTRootViewSizeFlexibilityWidthAndHeight:
return RCTSurfaceSizeMeasureModeWidthUndefined | RCTSurfaceSizeMeasureModeHeightUndefined;
case RCTRootViewSizeFlexibilityWidth:
return RCTSurfaceSizeMeasureModeWidthUndefined | RCTSurfaceSizeMeasureModeHeightExact;
case RCTRootViewSizeFlexibilityHeight:
return RCTSurfaceSizeMeasureModeWidthExact | RCTSurfaceSizeMeasureModeHeightUndefined;
case RCTRootViewSizeFlexibilityNone:
return RCTSurfaceSizeMeasureModeWidthExact | RCTSurfaceSizeMeasureModeHeightExact;
}
}
static RCTRootViewSizeFlexibility convertToRootViewSizeFlexibility(RCTSurfaceSizeMeasureMode sizeMeasureMode)
{
switch (sizeMeasureMode) {
case RCTSurfaceSizeMeasureModeWidthUndefined | RCTSurfaceSizeMeasureModeHeightUndefined:
return RCTRootViewSizeFlexibilityWidthAndHeight;
case RCTSurfaceSizeMeasureModeWidthUndefined | RCTSurfaceSizeMeasureModeHeightExact:
return RCTRootViewSizeFlexibilityWidth;
case RCTSurfaceSizeMeasureModeWidthExact | RCTSurfaceSizeMeasureModeHeightUndefined:
return RCTRootViewSizeFlexibilityHeight;
case RCTSurfaceSizeMeasureModeWidthExact | RCTSurfaceSizeMeasureModeHeightExact:
default:
return RCTRootViewSizeFlexibilityNone;
}
}
@implementation RCTSurfaceHostingProxyRootView
- (instancetype)initWithBridge:(RCTBridge *)bridge
moduleName:(NSString *)moduleName
initialProperties:(NSDictionary *)initialProperties
{
RCTAssertMainQueue();
RCTAssert(bridge, @"A bridge instance is required to create an RCTSurfaceHostingProxyRootView");
RCTAssert(moduleName, @"A moduleName is required to create an RCTSurfaceHostingProxyRootView");
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTSurfaceHostingProxyRootView init]", nil);
_bridge = bridge;
if (!bridge.isLoading) {
[bridge.performanceLogger markStartForTag:RCTPLTTI];
}
// `RCTRootViewSizeFlexibilityNone` is the RCTRootView's default.
RCTSurfaceSizeMeasureMode sizeMeasureMode = convertToSurfaceSizeMeasureMode(RCTRootViewSizeFlexibilityNone);
RCTSurface *surface = [[self class] createSurfaceWithBridge:bridge
moduleName:moduleName
initialProperties:initialProperties];
[surface start];
if (self = [super initWithSurface:surface sizeMeasureMode:sizeMeasureMode]) {
self.backgroundColor = [UIColor whiteColor];
}
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
return self;
}
- (instancetype)initWithBundleURL:(NSURL *)bundleURL
moduleName:(NSString *)moduleName
initialProperties:(NSDictionary *)initialProperties
launchOptions:(NSDictionary *)launchOptions
{
RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:bundleURL moduleProvider:nil launchOptions:launchOptions];
return [self initWithBridge:bridge moduleName:moduleName initialProperties:initialProperties];
}
RCT_NOT_IMPLEMENTED(-(instancetype)initWithFrame : (CGRect)frame)
RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : (NSCoder *)aDecoder)
#pragma mark proxy methods to RCTSurfaceHostingView
- (NSString *)moduleName
{
return super.surface.moduleName;
}
- (UIView *)contentView
{
return self;
}
- (NSNumber *)reactTag
{
return super.surface.rootViewTag;
}
- (RCTRootViewSizeFlexibility)sizeFlexibility
{
return convertToRootViewSizeFlexibility(super.sizeMeasureMode);
}
- (void)setSizeFlexibility:(RCTRootViewSizeFlexibility)sizeFlexibility
{
super.sizeMeasureMode = convertToSurfaceSizeMeasureMode(sizeFlexibility);
}
- (NSDictionary *)appProperties
{
return super.surface.properties;
}
- (void)setAppProperties:(NSDictionary *)appProperties
{
[super.surface setProperties:appProperties];
}
- (UIView *)loadingView
{
return super.activityIndicatorViewFactory ? super.activityIndicatorViewFactory() : nil;
}
- (void)setLoadingView:(UIView *)loadingView
{
super.activityIndicatorViewFactory = ^UIView *(void)
{
return loadingView;
};
}
#pragma mark RCTSurfaceDelegate proxying
- (void)surface:(RCTSurface *)surface didChangeStage:(RCTSurfaceStage)stage
{
[super surface:surface didChangeStage:stage];
if (RCTSurfaceStageIsRunning(stage)) {
[_bridge.performanceLogger markStopForTag:RCTPLTTI];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:RCTContentDidAppearNotification object:self];
});
}
}
- (void)surface:(RCTSurface *)surface didChangeIntrinsicSize:(CGSize)intrinsicSize
{
[super surface:surface didChangeIntrinsicSize:intrinsicSize];
[_delegate rootViewDidChangeIntrinsicSize:(RCTRootView *)self];
}
#pragma mark legacy
- (UIViewController *)reactViewController
{
return _reactViewController ?: [super reactViewController];
}
#pragma mark unsupported
- (void)cancelTouches
{
// Not supported.
}
@end

View File

@ -0,0 +1,77 @@
/*
* 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.
*/
#import <UIKit/UIKit.h>
#import <React/RCTSurfaceDelegate.h>
#import <React/RCTSurfaceSizeMeasureMode.h>
#import <React/RCTSurfaceStage.h>
@class RCTBridge;
@class RCTSurface;
typedef UIView *_Nullable (^RCTSurfaceHostingViewActivityIndicatorViewFactory)(void);
NS_ASSUME_NONNULL_BEGIN
/**
* UIView subclass which providers interoperability between UIKit and
* Surface regarding layout and life-cycle.
* This class can be used as easy-to-use general purpose integration point
* of ReactNative-powered experiences in UIKit based apps.
*/
@interface RCTSurfaceHostingView : UIView <RCTSurfaceDelegate>
/**
* Create an instance of RCTSurface to be hosted.
*/
+ (RCTSurface *)createSurfaceWithBridge:(RCTBridge *)bridge
moduleName:(NSString *)moduleName
initialProperties:(NSDictionary *)initialProperties;
/**
* Designated initializer.
* Instanciates a view with given Surface object.
* Note: The view retains the surface object.
*/
- (instancetype)initWithSurface:(RCTSurface *)surface
sizeMeasureMode:(RCTSurfaceSizeMeasureMode)sizeMeasureMode NS_DESIGNATED_INITIALIZER;
/**
* Convenience initializer.
* Instanciates a Surface object with given `bridge`, `moduleName`, and
* `initialProperties`, and then use it to instanciate a view.
*/
- (instancetype)initWithBridge:(RCTBridge *)bridge
moduleName:(NSString *)moduleName
initialProperties:(NSDictionary *)initialProperties
sizeMeasureMode:(RCTSurfaceSizeMeasureMode)sizeMeasureMode;
/**
* Surface object which is currently using to power the view.
* Read-only.
*/
@property (nonatomic, strong, readonly) RCTSurface *surface;
/**
* Size measure mode which are defining relationship between UIKit and ReactNative
* layout approaches.
* Defaults to `RCTSurfaceSizeMeasureModeWidthAtMost | RCTSurfaceSizeMeasureModeHeightAtMost`.
*/
@property (nonatomic, assign) RCTSurfaceSizeMeasureMode sizeMeasureMode;
/**
* Activity indicator factory.
* A hosting view may use this block to instantiate and display custom activity
* (loading) indicator (aka "spinner") when it needed.
* Defaults to `nil` (no activity indicator).
*/
@property (nonatomic, copy, nullable) RCTSurfaceHostingViewActivityIndicatorViewFactory activityIndicatorViewFactory;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,247 @@
/*
* 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.
*/
#import "RCTSurfaceHostingView.h"
#import "RCTConstants.h"
#import "RCTDefines.h"
#import "RCTSurface.h"
#import "RCTSurfaceDelegate.h"
#import "RCTSurfaceView.h"
#import "RCTUtils.h"
@interface RCTSurfaceHostingView ()
@property (nonatomic, assign) BOOL isActivityIndicatorViewVisible;
@property (nonatomic, assign) BOOL isSurfaceViewVisible;
@end
@implementation RCTSurfaceHostingView {
UIView *_Nullable _activityIndicatorView;
UIView *_Nullable _surfaceView;
RCTSurfaceStage _stage;
}
+ (RCTSurface *)createSurfaceWithBridge:(RCTBridge *)bridge
moduleName:(NSString *)moduleName
initialProperties:(NSDictionary *)initialProperties
{
return [[RCTSurface alloc] initWithBridge:bridge moduleName:moduleName initialProperties:initialProperties];
}
RCT_NOT_IMPLEMENTED(-(instancetype)init)
RCT_NOT_IMPLEMENTED(-(instancetype)initWithFrame : (CGRect)frame)
RCT_NOT_IMPLEMENTED(-(nullable instancetype)initWithCoder : (NSCoder *)coder)
- (instancetype)initWithBridge:(RCTBridge *)bridge
moduleName:(NSString *)moduleName
initialProperties:(NSDictionary *)initialProperties
sizeMeasureMode:(RCTSurfaceSizeMeasureMode)sizeMeasureMode
{
RCTSurface *surface = [[self class] createSurfaceWithBridge:bridge
moduleName:moduleName
initialProperties:initialProperties];
[surface start];
return [self initWithSurface:surface sizeMeasureMode:sizeMeasureMode];
}
- (instancetype)initWithSurface:(RCTSurface *)surface sizeMeasureMode:(RCTSurfaceSizeMeasureMode)sizeMeasureMode
{
if (self = [super initWithFrame:CGRectZero]) {
_surface = surface;
_sizeMeasureMode = sizeMeasureMode;
_surface.delegate = self;
_stage = surface.stage;
[self _updateViews];
}
return self;
}
- (void)dealloc
{
[_surface stop];
}
- (void)setFrame:(CGRect)frame
{
[super setFrame:frame];
CGSize minimumSize;
CGSize maximumSize;
RCTSurfaceMinimumSizeAndMaximumSizeFromSizeAndSizeMeasureMode(
self.bounds.size, _sizeMeasureMode, &minimumSize, &maximumSize);
[_surface setMinimumSize:minimumSize maximumSize:maximumSize];
}
- (CGSize)intrinsicContentSize
{
if (RCTSurfaceStageIsPreparing(_stage)) {
if (_activityIndicatorView) {
return _activityIndicatorView.intrinsicContentSize;
}
return CGSizeZero;
}
return _surface.intrinsicSize;
}
- (CGSize)sizeThatFits:(CGSize)size
{
if (RCTSurfaceStageIsPreparing(_stage)) {
if (_activityIndicatorView) {
return [_activityIndicatorView sizeThatFits:size];
}
return CGSizeZero;
}
CGSize minimumSize;
CGSize maximumSize;
RCTSurfaceMinimumSizeAndMaximumSizeFromSizeAndSizeMeasureMode(size, _sizeMeasureMode, &minimumSize, &maximumSize);
return [_surface sizeThatFitsMinimumSize:minimumSize maximumSize:maximumSize];
}
- (void)setStage:(RCTSurfaceStage)stage
{
if (stage == _stage) {
return;
}
BOOL shouldInvalidateLayout = RCTSurfaceStageIsRunning(stage) != RCTSurfaceStageIsRunning(_stage) ||
RCTSurfaceStageIsPreparing(stage) != RCTSurfaceStageIsPreparing(_stage);
_stage = stage;
if (shouldInvalidateLayout) {
[self _invalidateLayout];
[self _updateViews];
}
}
- (void)setSizeMeasureMode:(RCTSurfaceSizeMeasureMode)sizeMeasureMode
{
if (sizeMeasureMode == _sizeMeasureMode) {
return;
}
_sizeMeasureMode = sizeMeasureMode;
[self _invalidateLayout];
}
#pragma mark - isActivityIndicatorViewVisible
- (void)setIsActivityIndicatorViewVisible:(BOOL)visible
{
if (_isActivityIndicatorViewVisible == visible) {
return;
}
_isActivityIndicatorViewVisible = visible;
if (visible) {
if (_activityIndicatorViewFactory) {
_activityIndicatorView = _activityIndicatorViewFactory();
_activityIndicatorView.frame = self.bounds;
_activityIndicatorView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self addSubview:_activityIndicatorView];
}
} else {
[_activityIndicatorView removeFromSuperview];
_activityIndicatorView = nil;
}
}
#pragma mark - isSurfaceViewVisible
- (void)setIsSurfaceViewVisible:(BOOL)visible
{
if (_isSurfaceViewVisible == visible) {
return;
}
_isSurfaceViewVisible = visible;
if (visible) {
_surfaceView = _surface.view;
_surfaceView.frame = self.bounds;
_surfaceView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self addSubview:_surfaceView];
} else {
[_surfaceView removeFromSuperview];
_surfaceView = nil;
}
}
#pragma mark - activityIndicatorViewFactory
- (void)setActivityIndicatorViewFactory:(RCTSurfaceHostingViewActivityIndicatorViewFactory)activityIndicatorViewFactory
{
_activityIndicatorViewFactory = activityIndicatorViewFactory;
if (_isActivityIndicatorViewVisible) {
self.isActivityIndicatorViewVisible = NO;
self.isActivityIndicatorViewVisible = YES;
}
}
#pragma mark - UITraitCollection updates
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
{
[super traitCollectionDidChange:previousTraitCollection];
[[NSNotificationCenter defaultCenter]
postNotificationName:RCTUserInterfaceStyleDidChangeNotification
object:self
userInfo:@{
RCTUserInterfaceStyleDidChangeNotificationTraitCollectionKey : self.traitCollection,
}];
}
#pragma mark - Private stuff
- (void)_invalidateLayout
{
[self invalidateIntrinsicContentSize];
[self.superview setNeedsLayout];
}
- (void)_updateViews
{
self.isSurfaceViewVisible = RCTSurfaceStageIsRunning(_stage);
self.isActivityIndicatorViewVisible = RCTSurfaceStageIsPreparing(_stage);
}
- (void)didMoveToWindow
{
[super didMoveToWindow];
[self _updateViews];
}
#pragma mark - RCTSurfaceDelegate
- (void)surface:(__unused RCTSurface *)surface didChangeStage:(RCTSurfaceStage)stage
{
RCTExecuteOnMainQueue(^{
[self setStage:stage];
});
}
- (void)surface:(__unused RCTSurface *)surface didChangeIntrinsicSize:(__unused CGSize)intrinsicSize
{
RCTExecuteOnMainQueue(^{
[self _invalidateLayout];
});
}
@end

View File

@ -0,0 +1,32 @@
/*
* 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.
*/
#import <UIKit/UIKit.h>
#import <React/RCTDefines.h>
/**
* Bitmask defines how size constrains from `-[UIView sizeThatFits:]`
* are translated to `-[RCTSurface sizeThatFitsMinimumSize:maximumSize:]`.
*/
typedef NS_OPTIONS(NSInteger, RCTSurfaceSizeMeasureMode) {
RCTSurfaceSizeMeasureModeWidthUndefined = 0 << 0,
RCTSurfaceSizeMeasureModeWidthExact = 1 << 0,
RCTSurfaceSizeMeasureModeWidthAtMost = 2 << 0,
RCTSurfaceSizeMeasureModeHeightUndefined = 0 << 2,
RCTSurfaceSizeMeasureModeHeightExact = 1 << 2,
RCTSurfaceSizeMeasureModeHeightAtMost = 2 << 2,
};
/**
* Returns size constraints based on `size` and `sizeMeasureMode`.
*/
RCT_EXTERN void RCTSurfaceMinimumSizeAndMaximumSizeFromSizeAndSizeMeasureMode(
CGSize size,
RCTSurfaceSizeMeasureMode sizeMeasureMode,
CGSize *minimumSize,
CGSize *maximumSize);

View File

@ -0,0 +1,34 @@
/*
* 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.
*/
#import <UIKit/UIKit.h>
#import "RCTSurfaceSizeMeasureMode.h"
void RCTSurfaceMinimumSizeAndMaximumSizeFromSizeAndSizeMeasureMode(
CGSize size,
RCTSurfaceSizeMeasureMode sizeMeasureMode,
CGSize *minimumSize,
CGSize *maximumSize)
{
*minimumSize = CGSizeZero;
*maximumSize = CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX);
if (sizeMeasureMode & RCTSurfaceSizeMeasureModeWidthExact) {
minimumSize->width = size.width;
maximumSize->width = size.width;
} else if (sizeMeasureMode & RCTSurfaceSizeMeasureModeWidthAtMost) {
maximumSize->width = size.width;
}
if (sizeMeasureMode & RCTSurfaceSizeMeasureModeHeightExact) {
minimumSize->height = size.height;
maximumSize->height = size.height;
} else if (sizeMeasureMode & RCTSurfaceSizeMeasureModeHeightAtMost) {
maximumSize->height = size.height;
}
}