-
Notifications
You must be signed in to change notification settings - Fork 24.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introducing RCTSurfaceHostingComponent
Summary: RCTSurfaceHostingComponent is ComponentKit component represents given Surface instance. Differential Revision: D6217104 fbshipit-source-id: 50805d97e744de24a188bc97b33de4709e785aae
- Loading branch information
1 parent
6d92046
commit e75bd87
Showing
8 changed files
with
392 additions
and
0 deletions.
There are no files selected for viewing
22 changes: 22 additions & 0 deletions
22
Libraries/SurfaceHostingComponent/RCTSurfaceHostingComponent+Internal.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,22 @@ | ||
/** | ||
* 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 <RCTSurfaceHostingComponent/RCTSurfaceHostingComponent.h> | ||
#import <RCTSurfaceHostingComponent/RCTSurfaceHostingComponentOptions.h> | ||
|
||
@class RCTSurface; | ||
@class RCTSurfaceHostingComponentState; | ||
|
||
@interface RCTSurfaceHostingComponent () | ||
|
||
@property (nonatomic, strong, readonly) RCTSurface *surface; | ||
@property (nonatomic, retain, readonly) RCTSurfaceHostingComponentState *state; | ||
@property (nonatomic, assign, readonly) RCTSurfaceHostingComponentOptions options; | ||
|
||
@end |
22 changes: 22 additions & 0 deletions
22
Libraries/SurfaceHostingComponent/RCTSurfaceHostingComponent.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,22 @@ | ||
/** | ||
* 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 <ComponentKit/CKComponent.h> | ||
#import <RCTSurfaceHostingComponent/RCTSurfaceHostingComponentOptions.h> | ||
|
||
@class RCTSurface; | ||
|
||
/** | ||
* ComponentKit component represents given Surface instance. | ||
*/ | ||
@interface RCTSurfaceHostingComponent : CKComponent | ||
|
||
+ (instancetype)newWithSurface:(RCTSurface *)surface options:(RCTSurfaceHostingComponentOptions)options; | ||
|
||
@end |
104 changes: 104 additions & 0 deletions
104
Libraries/SurfaceHostingComponent/RCTSurfaceHostingComponent.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,104 @@ | ||
/** | ||
* 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 "RCTSurfaceHostingComponent.h" | ||
#import "RCTSurfaceHostingComponent+Internal.h" | ||
|
||
#import <UIKit/UIKit.h> | ||
|
||
#import <ComponentKit/CKComponentSubclass.h> | ||
#import <React/RCTSurface.h> | ||
#import <React/RCTSurfaceView.h> | ||
|
||
#import "RCTSurfaceHostingComponentState.h" | ||
|
||
@implementation RCTSurfaceHostingComponent | ||
|
||
+ (id)initialState | ||
{ | ||
return [RCTSurfaceHostingComponentState new]; | ||
} | ||
|
||
+ (instancetype)newWithSurface:(RCTSurface *)surface options:(RCTSurfaceHostingComponentOptions)options | ||
{ | ||
CKComponentScope scope(self, surface); | ||
|
||
RCTSurfaceHostingComponentState *const state = scope.state(); | ||
|
||
RCTSurfaceHostingComponentState *const newState = | ||
[RCTSurfaceHostingComponentState newWithStage:surface.stage | ||
intrinsicSize:surface.intrinsicSize]; | ||
|
||
if (![state isEqual:newState]) { | ||
CKComponentScope::replaceState(scope, newState); | ||
} | ||
|
||
RCTSurfaceHostingComponent *const component = | ||
[super newWithView:{[UIView class]} size:{}]; | ||
|
||
if (component) { | ||
component->_state = scope.state(); | ||
component->_surface = surface; | ||
component->_options = options; | ||
} | ||
|
||
return component; | ||
} | ||
|
||
- (CKComponentLayout)computeLayoutThatFits:(CKSizeRange)constrainedSize | ||
{ | ||
// Optimistically communicating layout constraints to the `_surface`, | ||
// just to provide layout constraints to React Native as early as possible. | ||
// React Native *may* use this info later during applying the own state and | ||
// related laying out in parallel with ComponentKit execution. | ||
// This call will not interfere (or introduce any negative side effects) with | ||
// following invocation of `sizeThatFitsMinimumSize:maximumSize:`. | ||
// A weak point: We assume here that this particular layout will be | ||
// mounted eventually, which is technically not guaranteed by ComponentKit. | ||
// Therefore we also assume that the last layout calculated in a sequence | ||
// will be mounted anyways, which is probably true for all *real* use cases. | ||
// We plan to tackle this problem during the next big step in improving | ||
// interop compatibilities of React Native which will enable us granularly | ||
// control React Native mounting blocks and, as a result, implement | ||
// truly synchronous mounting stage between React Native and ComponentKit. | ||
[_surface setMinimumSize:constrainedSize.min | ||
maximumSize:constrainedSize.max]; | ||
|
||
// Just in case of the very first building pass, we give React Native a chance | ||
// to prepare its internals for coming synchronous measuring. | ||
[_surface synchronouslyWaitForStage:RCTSurfaceStageSurfaceDidInitialLayout | ||
timeout:_options.synchronousLayoutingTimeout]; | ||
|
||
CGSize fittingSize = CGSizeZero; | ||
if (_surface.stage & RCTSurfaceStageSurfaceDidInitialLayout) { | ||
fittingSize = [_surface sizeThatFitsMinimumSize:constrainedSize.min | ||
maximumSize:constrainedSize.max]; | ||
} | ||
else { | ||
fittingSize = _options.activityIndicatorSize; | ||
} | ||
|
||
fittingSize = constrainedSize.clamp(fittingSize); | ||
return {self, fittingSize}; | ||
} | ||
|
||
- (CKComponentBoundsAnimation)boundsAnimationFromPreviousComponent:(RCTSurfaceHostingComponent *)previousComponent | ||
{ | ||
if (_options.boundsAnimations && (previousComponent->_state.stage != _state.stage)) { | ||
return { | ||
.mode = CKComponentBoundsAnimationModeDefault, | ||
.duration = 0.25, | ||
.options = UIViewAnimationOptionCurveEaseInOut, | ||
}; | ||
} | ||
|
||
return {}; | ||
} | ||
|
||
@end |
14 changes: 14 additions & 0 deletions
14
Libraries/SurfaceHostingComponent/RCTSurfaceHostingComponentController.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,14 @@ | ||
/** | ||
* 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 <ComponentKit/CKComponentController.h> | ||
|
||
@interface RCTSurfaceHostingComponentController : CKComponentController | ||
|
||
@end |
143 changes: 143 additions & 0 deletions
143
Libraries/SurfaceHostingComponent/RCTSurfaceHostingComponentController.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,143 @@ | ||
/** | ||
* 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 "RCTSurfaceHostingComponentController.h" | ||
|
||
#import <ComponentKit/CKComponentSubclass.h> | ||
#import <React/RCTAssert.h> | ||
#import <React/RCTSurface.h> | ||
#import <React/RCTSurfaceDelegate.h> | ||
#import <React/RCTSurfaceView.h> | ||
|
||
#import "RCTSurfaceHostingComponent+Internal.h" | ||
#import "RCTSurfaceHostingComponent.h" | ||
#import "RCTSurfaceHostingComponentState.h" | ||
|
||
@interface RCTSurfaceHostingComponentController() <RCTSurfaceDelegate> | ||
@end | ||
|
||
@implementation RCTSurfaceHostingComponentController { | ||
RCTSurface *_surface; | ||
} | ||
|
||
- (instancetype)initWithComponent:(RCTSurfaceHostingComponent *)component | ||
{ | ||
if (self = [super initWithComponent:component]) { | ||
[self updateSurfaceWithComponent:component]; | ||
} | ||
|
||
return self; | ||
} | ||
|
||
#pragma mark - Lifecycle | ||
|
||
- (void)didMount | ||
{ | ||
[super didMount]; | ||
[self mountSurfaceView]; | ||
} | ||
|
||
- (void)didRemount | ||
{ | ||
[super didRemount]; | ||
[self mountSurfaceView]; | ||
} | ||
|
||
- (void)didUpdateComponent | ||
{ | ||
[super didUpdateComponent]; | ||
[self updateSurfaceWithComponent:(RCTSurfaceHostingComponent *)self.component]; | ||
} | ||
|
||
- (void)didUnmount | ||
{ | ||
[super didUnmount]; | ||
[self unmountSurfaceView]; | ||
} | ||
|
||
#pragma mark - Helpers | ||
|
||
- (void)updateSurfaceWithComponent:(RCTSurfaceHostingComponent *)component | ||
{ | ||
// Updating `surface` | ||
RCTSurface *const surface = component.surface; | ||
if (surface != _surface) { | ||
if (_surface.delegate == self) { | ||
_surface.delegate = nil; | ||
} | ||
|
||
_surface = surface; | ||
_surface.delegate = self; | ||
} | ||
} | ||
|
||
- (void)setIntrinsicSize:(CGSize)intrinsicSize | ||
{ | ||
[self.component updateState:^(RCTSurfaceHostingComponentState *state) { | ||
return [RCTSurfaceHostingComponentState newWithStage:state.stage | ||
intrinsicSize:intrinsicSize]; | ||
} mode:[self suitableStateUpdateMode]]; | ||
} | ||
|
||
- (void)setStage:(RCTSurfaceStage)stage | ||
{ | ||
[self.component updateState:^(RCTSurfaceHostingComponentState *state) { | ||
return [RCTSurfaceHostingComponentState newWithStage:stage | ||
intrinsicSize:state.intrinsicSize]; | ||
} mode:[self suitableStateUpdateMode]]; | ||
} | ||
|
||
- (CKUpdateMode)suitableStateUpdateMode | ||
{ | ||
return ((RCTSurfaceHostingComponent *)self.component).options.synchronousStateUpdates && RCTIsMainQueue() ? CKUpdateModeSynchronous : CKUpdateModeAsynchronous; | ||
} | ||
|
||
- (void)mountSurfaceView | ||
{ | ||
UIView *const surfaceView = _surface.view; | ||
|
||
const CKComponentViewContext &context = [[self component] viewContext]; | ||
|
||
UIView *const superview = context.view; | ||
superview.clipsToBounds = YES; | ||
|
||
RCTAssert([superview.subviews count] <= 1, @"Should never have more than a single stateful subview."); | ||
|
||
UIView *const existingSurfaceView = [superview.subviews lastObject]; | ||
if (existingSurfaceView != surfaceView) { | ||
[existingSurfaceView removeFromSuperview]; | ||
surfaceView.frame = superview.bounds; | ||
surfaceView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; | ||
[superview addSubview:surfaceView]; | ||
} | ||
} | ||
|
||
- (void)unmountSurfaceView | ||
{ | ||
const CKComponentViewContext &context = [[self component] viewContext]; | ||
|
||
UIView *const superview = context.view; | ||
RCTAssert([superview.subviews count] <= 1, @"Should never have more than a single stateful subview."); | ||
UIView *const existingSurfaceView = [superview.subviews lastObject]; | ||
[existingSurfaceView removeFromSuperview]; | ||
} | ||
|
||
#pragma mark - RCTSurfaceDelegate | ||
|
||
- (void)surface:(RCTSurface *)surface didChangeIntrinsicSize:(CGSize)intrinsicSize | ||
{ | ||
[self setIntrinsicSize:intrinsicSize]; | ||
} | ||
|
||
- (void)surface:(RCTSurface *)surface didChangeStage:(RCTSurfaceStage)stage | ||
{ | ||
[self setStage:stage]; | ||
} | ||
|
||
@end |
22 changes: 22 additions & 0 deletions
22
Libraries/SurfaceHostingComponent/RCTSurfaceHostingComponentOptions.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,22 @@ | ||
/** | ||
* 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 <ComponentKit/CKComponent.h> | ||
|
||
typedef CKComponent *(^RCTSurfaceHostingComponentOptionsActivityIndicatorComponentFactory)(); | ||
|
||
struct RCTSurfaceHostingComponentOptions { | ||
NSTimeInterval synchronousLayoutingTimeout = 0.350; | ||
BOOL synchronousStateUpdates = YES; | ||
CGSize activityIndicatorSize = {44.0, 44.0}; | ||
BOOL boundsAnimations = YES; | ||
RCTSurfaceHostingComponentOptionsActivityIndicatorComponentFactory activityIndicatorComponentFactory = nil; | ||
}; |
22 changes: 22 additions & 0 deletions
22
Libraries/SurfaceHostingComponent/RCTSurfaceHostingComponentState.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,22 @@ | ||
/** | ||
* 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/RCTSurfaceStage.h> | ||
|
||
@interface RCTSurfaceHostingComponentState: NSObject | ||
|
||
@property (nonatomic, readonly, assign) CGSize intrinsicSize; | ||
@property (nonatomic, readonly, assign) RCTSurfaceStage stage; | ||
|
||
+ (instancetype)newWithStage:(RCTSurfaceStage)stage | ||
intrinsicSize:(CGSize)intrinsicSize; | ||
|
||
@end |
43 changes: 43 additions & 0 deletions
43
Libraries/SurfaceHostingComponent/RCTSurfaceHostingComponentState.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,43 @@ | ||
/** | ||
* 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 "RCTSurfaceHostingComponentState.h" | ||
|
||
@implementation RCTSurfaceHostingComponentState | ||
|
||
+ (instancetype)newWithStage:(RCTSurfaceStage)stage | ||
intrinsicSize:(CGSize)intrinsicSize | ||
{ | ||
return [[self alloc] initWithStage:stage intrinsicSize:intrinsicSize]; | ||
} | ||
|
||
|
||
- (instancetype)initWithStage:(RCTSurfaceStage)stage | ||
intrinsicSize:(CGSize)intrinsicSize | ||
{ | ||
if (self = [super init]) { | ||
_stage = stage; | ||
_intrinsicSize = intrinsicSize; | ||
} | ||
|
||
return self; | ||
} | ||
|
||
- (BOOL)isEqual:(RCTSurfaceHostingComponentState *)other | ||
{ | ||
if (other == self) { | ||
return YES; | ||
} | ||
|
||
return | ||
_stage == other->_stage && | ||
CGSizeEqualToSize(_intrinsicSize, other->_intrinsicSize); | ||
} | ||
|
||
@end |