-
Notifications
You must be signed in to change notification settings - Fork 24.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
RCTSurface: RCTSurfaceHostingView, interop layer beteween UIKit and R…
…CTSurface Summary: UIView subclass which providers interoperability between UIKit and Surface regarding layout and life-cycle. Reviewed By: rsnara Differential Revision: D6465922 fbshipit-source-id: 2a7cfb70119d460bc22968d1aa780833bf47d7f6
- Loading branch information
1 parent
c8e72bb
commit 4d37cf0
Showing
5 changed files
with
380 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
69 changes: 69 additions & 0 deletions
69
React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingView.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
/** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the BSD-style license found in the | ||
* LICENSE file in the root directory of this source tree. An additional grant | ||
* of patent rights can be found in the PATENTS file in the same directory. | ||
*/ | ||
|
||
#import <UIKit/UIKit.h> | ||
|
||
#import <React/RCTSurfaceSizeMeasureMode.h> | ||
#import <React/RCTSurfaceStage.h> | ||
|
||
@class RCTBridge; | ||
@class RCTSurface; | ||
|
||
typedef UIView *(^RCTSurfaceHostingViewActivityIndicatorViewFactory)(); | ||
|
||
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 | ||
|
||
/** | ||
* Designated initializer. | ||
* Instanciates a view with given Surface object. | ||
* Note: The view retains the surface object. | ||
*/ | ||
- (instancetype)initWithSurface:(RCTSurface *)surface 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; | ||
|
||
/** | ||
* 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 |
224 changes: 224 additions & 0 deletions
224
React/Base/Surface/SurfaceHostingView/RCTSurfaceHostingView.mm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
/** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the BSD-style license found in the | ||
* LICENSE file in the root directory of this source tree. An additional grant | ||
* of patent rights can be found in the PATENTS file in the same directory. | ||
*/ | ||
|
||
#import "RCTSurfaceHostingView.h" | ||
|
||
#import "RCTDefines.h" | ||
#import "RCTSurface.h" | ||
#import "RCTSurfaceDelegate.h" | ||
#import "RCTSurfaceView.h" | ||
#import "RCTUtils.h" | ||
|
||
@interface RCTSurfaceHostingView () <RCTSurfaceDelegate> | ||
|
||
@property (nonatomic, assign) BOOL isActivityIndicatorViewVisible; | ||
@property (nonatomic, assign) BOOL isSurfaceViewVisible; | ||
|
||
@end | ||
|
||
@implementation RCTSurfaceHostingView { | ||
UIView *_Nullable _activityIndicatorView; | ||
UIView *_Nullable _surfaceView; | ||
RCTSurfaceStage _stage; | ||
} | ||
|
||
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 | ||
{ | ||
RCTSurface *surface = | ||
[[RCTSurface alloc] initWithBridge:bridge | ||
moduleName:moduleName | ||
initialProperties:initialProperties]; | ||
|
||
return [self initWithSurface:surface]; | ||
} | ||
|
||
- (instancetype)initWithSurface:(RCTSurface *)surface | ||
{ | ||
if (self = [super initWithFrame:CGRectZero]) { | ||
_surface = surface; | ||
|
||
_sizeMeasureMode = | ||
RCTSurfaceSizeMeasureModeWidthAtMost | | ||
RCTSurfaceSizeMeasureModeHeightAtMost; | ||
|
||
_surface.delegate = self; | ||
_stage = surface.stage; | ||
[self _updateViews]; | ||
} | ||
|
||
return self; | ||
} | ||
|
||
- (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 minumumSize = CGSizeZero; | ||
CGSize maximumSize = CGSizeMake(INFINITY, INFINITY); | ||
|
||
if (_sizeMeasureMode & RCTSurfaceSizeMeasureModeWidthExact) { | ||
minumumSize.width = size.width; | ||
maximumSize.width = size.width; | ||
} | ||
else if (_sizeMeasureMode & RCTSurfaceSizeMeasureModeWidthAtMost) { | ||
maximumSize.width = size.width; | ||
} | ||
|
||
if (_sizeMeasureMode & RCTSurfaceSizeMeasureModeHeightExact) { | ||
minumumSize.height = size.height; | ||
maximumSize.height = size.height; | ||
} | ||
else if (_sizeMeasureMode & RCTSurfaceSizeMeasureModeHeightAtMost) { | ||
maximumSize.height = size.height; | ||
} | ||
|
||
return [_surface sizeThatFitsMinimumSize:minumumSize | ||
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; | ||
} | ||
|
||
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; | ||
} | ||
|
||
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) { | ||
_isActivityIndicatorViewVisible = NO; | ||
self.isActivityIndicatorViewVisible = YES; | ||
} | ||
} | ||
|
||
#pragma mark - Private stuff | ||
|
||
- (void)_invalidateLayout | ||
{ | ||
[self.superview setNeedsLayout]; | ||
} | ||
|
||
- (void)_updateViews | ||
{ | ||
self.isSurfaceViewVisible = RCTSurfaceStageIsRunning(_stage); | ||
self.isActivityIndicatorViewVisible = RCTSurfaceStageIsPreparing(_stage); | ||
} | ||
|
||
- (void)didMoveToWindow | ||
{ | ||
[super didMoveToWindow]; | ||
[self _updateViews]; | ||
} | ||
|
||
#pragma mark - RCTSurfaceDelegate | ||
|
||
- (void)surface:(RCTSurface *)surface didChangeStage:(RCTSurfaceStage)stage | ||
{ | ||
RCTExecuteOnMainQueue(^{ | ||
[self setStage:stage]; | ||
}); | ||
} | ||
|
||
- (void)surface:(RCTSurface *)surface didChangeIntrinsicSize:(CGSize)intrinsicSize | ||
{ | ||
RCTExecuteOnMainQueue(^{ | ||
[self _invalidateLayout]; | ||
}); | ||
} | ||
|
||
@end |
23 changes: 23 additions & 0 deletions
23
React/Base/Surface/SurfaceHostingView/RCTSurfaceSizeMeasureMode.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the BSD-style license found in the | ||
* LICENSE file in the root directory of this source tree. An additional grant | ||
* of patent rights can be found in the PATENTS file in the same directory. | ||
*/ | ||
|
||
#import <UIKit/UIKit.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, | ||
}; |
Oops, something went wrong.
4d37cf0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @talkol
Hey Tal!
You can try to use it for implementing truly sync initial rendering/layout!
react-native/React/Base/Surface/RCTSurface.mm
Lines 451 to 454 in 4d37cf0
RCTSurface
object;RCTSurfaceHostingView
using the surface;synchronouslyWaitForStage:timeout:
where you need sync waiting;I would love to hear any feedback. 😄