Skip to content

Commit e75bd87

Browse files
sherginfacebook-github-bot
authored andcommitted
Introducing RCTSurfaceHostingComponent
Summary: RCTSurfaceHostingComponent is ComponentKit component represents given Surface instance. Differential Revision: D6217104 fbshipit-source-id: 50805d97e744de24a188bc97b33de4709e785aae
1 parent 6d92046 commit e75bd87

8 files changed

+392
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
#import <RCTSurfaceHostingComponent/RCTSurfaceHostingComponent.h>
11+
#import <RCTSurfaceHostingComponent/RCTSurfaceHostingComponentOptions.h>
12+
13+
@class RCTSurface;
14+
@class RCTSurfaceHostingComponentState;
15+
16+
@interface RCTSurfaceHostingComponent ()
17+
18+
@property (nonatomic, strong, readonly) RCTSurface *surface;
19+
@property (nonatomic, retain, readonly) RCTSurfaceHostingComponentState *state;
20+
@property (nonatomic, assign, readonly) RCTSurfaceHostingComponentOptions options;
21+
22+
@end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
#import <ComponentKit/CKComponent.h>
11+
#import <RCTSurfaceHostingComponent/RCTSurfaceHostingComponentOptions.h>
12+
13+
@class RCTSurface;
14+
15+
/**
16+
* ComponentKit component represents given Surface instance.
17+
*/
18+
@interface RCTSurfaceHostingComponent : CKComponent
19+
20+
+ (instancetype)newWithSurface:(RCTSurface *)surface options:(RCTSurfaceHostingComponentOptions)options;
21+
22+
@end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
#import "RCTSurfaceHostingComponent.h"
11+
#import "RCTSurfaceHostingComponent+Internal.h"
12+
13+
#import <UIKit/UIKit.h>
14+
15+
#import <ComponentKit/CKComponentSubclass.h>
16+
#import <React/RCTSurface.h>
17+
#import <React/RCTSurfaceView.h>
18+
19+
#import "RCTSurfaceHostingComponentState.h"
20+
21+
@implementation RCTSurfaceHostingComponent
22+
23+
+ (id)initialState
24+
{
25+
return [RCTSurfaceHostingComponentState new];
26+
}
27+
28+
+ (instancetype)newWithSurface:(RCTSurface *)surface options:(RCTSurfaceHostingComponentOptions)options
29+
{
30+
CKComponentScope scope(self, surface);
31+
32+
RCTSurfaceHostingComponentState *const state = scope.state();
33+
34+
RCTSurfaceHostingComponentState *const newState =
35+
[RCTSurfaceHostingComponentState newWithStage:surface.stage
36+
intrinsicSize:surface.intrinsicSize];
37+
38+
if (![state isEqual:newState]) {
39+
CKComponentScope::replaceState(scope, newState);
40+
}
41+
42+
RCTSurfaceHostingComponent *const component =
43+
[super newWithView:{[UIView class]} size:{}];
44+
45+
if (component) {
46+
component->_state = scope.state();
47+
component->_surface = surface;
48+
component->_options = options;
49+
}
50+
51+
return component;
52+
}
53+
54+
- (CKComponentLayout)computeLayoutThatFits:(CKSizeRange)constrainedSize
55+
{
56+
// Optimistically communicating layout constraints to the `_surface`,
57+
// just to provide layout constraints to React Native as early as possible.
58+
// React Native *may* use this info later during applying the own state and
59+
// related laying out in parallel with ComponentKit execution.
60+
// This call will not interfere (or introduce any negative side effects) with
61+
// following invocation of `sizeThatFitsMinimumSize:maximumSize:`.
62+
// A weak point: We assume here that this particular layout will be
63+
// mounted eventually, which is technically not guaranteed by ComponentKit.
64+
// Therefore we also assume that the last layout calculated in a sequence
65+
// will be mounted anyways, which is probably true for all *real* use cases.
66+
// We plan to tackle this problem during the next big step in improving
67+
// interop compatibilities of React Native which will enable us granularly
68+
// control React Native mounting blocks and, as a result, implement
69+
// truly synchronous mounting stage between React Native and ComponentKit.
70+
[_surface setMinimumSize:constrainedSize.min
71+
maximumSize:constrainedSize.max];
72+
73+
// Just in case of the very first building pass, we give React Native a chance
74+
// to prepare its internals for coming synchronous measuring.
75+
[_surface synchronouslyWaitForStage:RCTSurfaceStageSurfaceDidInitialLayout
76+
timeout:_options.synchronousLayoutingTimeout];
77+
78+
CGSize fittingSize = CGSizeZero;
79+
if (_surface.stage & RCTSurfaceStageSurfaceDidInitialLayout) {
80+
fittingSize = [_surface sizeThatFitsMinimumSize:constrainedSize.min
81+
maximumSize:constrainedSize.max];
82+
}
83+
else {
84+
fittingSize = _options.activityIndicatorSize;
85+
}
86+
87+
fittingSize = constrainedSize.clamp(fittingSize);
88+
return {self, fittingSize};
89+
}
90+
91+
- (CKComponentBoundsAnimation)boundsAnimationFromPreviousComponent:(RCTSurfaceHostingComponent *)previousComponent
92+
{
93+
if (_options.boundsAnimations && (previousComponent->_state.stage != _state.stage)) {
94+
return {
95+
.mode = CKComponentBoundsAnimationModeDefault,
96+
.duration = 0.25,
97+
.options = UIViewAnimationOptionCurveEaseInOut,
98+
};
99+
}
100+
101+
return {};
102+
}
103+
104+
@end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
#import <ComponentKit/CKComponentController.h>
11+
12+
@interface RCTSurfaceHostingComponentController : CKComponentController
13+
14+
@end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
#import "RCTSurfaceHostingComponentController.h"
11+
12+
#import <ComponentKit/CKComponentSubclass.h>
13+
#import <React/RCTAssert.h>
14+
#import <React/RCTSurface.h>
15+
#import <React/RCTSurfaceDelegate.h>
16+
#import <React/RCTSurfaceView.h>
17+
18+
#import "RCTSurfaceHostingComponent+Internal.h"
19+
#import "RCTSurfaceHostingComponent.h"
20+
#import "RCTSurfaceHostingComponentState.h"
21+
22+
@interface RCTSurfaceHostingComponentController() <RCTSurfaceDelegate>
23+
@end
24+
25+
@implementation RCTSurfaceHostingComponentController {
26+
RCTSurface *_surface;
27+
}
28+
29+
- (instancetype)initWithComponent:(RCTSurfaceHostingComponent *)component
30+
{
31+
if (self = [super initWithComponent:component]) {
32+
[self updateSurfaceWithComponent:component];
33+
}
34+
35+
return self;
36+
}
37+
38+
#pragma mark - Lifecycle
39+
40+
- (void)didMount
41+
{
42+
[super didMount];
43+
[self mountSurfaceView];
44+
}
45+
46+
- (void)didRemount
47+
{
48+
[super didRemount];
49+
[self mountSurfaceView];
50+
}
51+
52+
- (void)didUpdateComponent
53+
{
54+
[super didUpdateComponent];
55+
[self updateSurfaceWithComponent:(RCTSurfaceHostingComponent *)self.component];
56+
}
57+
58+
- (void)didUnmount
59+
{
60+
[super didUnmount];
61+
[self unmountSurfaceView];
62+
}
63+
64+
#pragma mark - Helpers
65+
66+
- (void)updateSurfaceWithComponent:(RCTSurfaceHostingComponent *)component
67+
{
68+
// Updating `surface`
69+
RCTSurface *const surface = component.surface;
70+
if (surface != _surface) {
71+
if (_surface.delegate == self) {
72+
_surface.delegate = nil;
73+
}
74+
75+
_surface = surface;
76+
_surface.delegate = self;
77+
}
78+
}
79+
80+
- (void)setIntrinsicSize:(CGSize)intrinsicSize
81+
{
82+
[self.component updateState:^(RCTSurfaceHostingComponentState *state) {
83+
return [RCTSurfaceHostingComponentState newWithStage:state.stage
84+
intrinsicSize:intrinsicSize];
85+
} mode:[self suitableStateUpdateMode]];
86+
}
87+
88+
- (void)setStage:(RCTSurfaceStage)stage
89+
{
90+
[self.component updateState:^(RCTSurfaceHostingComponentState *state) {
91+
return [RCTSurfaceHostingComponentState newWithStage:stage
92+
intrinsicSize:state.intrinsicSize];
93+
} mode:[self suitableStateUpdateMode]];
94+
}
95+
96+
- (CKUpdateMode)suitableStateUpdateMode
97+
{
98+
return ((RCTSurfaceHostingComponent *)self.component).options.synchronousStateUpdates && RCTIsMainQueue() ? CKUpdateModeSynchronous : CKUpdateModeAsynchronous;
99+
}
100+
101+
- (void)mountSurfaceView
102+
{
103+
UIView *const surfaceView = _surface.view;
104+
105+
const CKComponentViewContext &context = [[self component] viewContext];
106+
107+
UIView *const superview = context.view;
108+
superview.clipsToBounds = YES;
109+
110+
RCTAssert([superview.subviews count] <= 1, @"Should never have more than a single stateful subview.");
111+
112+
UIView *const existingSurfaceView = [superview.subviews lastObject];
113+
if (existingSurfaceView != surfaceView) {
114+
[existingSurfaceView removeFromSuperview];
115+
surfaceView.frame = superview.bounds;
116+
surfaceView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
117+
[superview addSubview:surfaceView];
118+
}
119+
}
120+
121+
- (void)unmountSurfaceView
122+
{
123+
const CKComponentViewContext &context = [[self component] viewContext];
124+
125+
UIView *const superview = context.view;
126+
RCTAssert([superview.subviews count] <= 1, @"Should never have more than a single stateful subview.");
127+
UIView *const existingSurfaceView = [superview.subviews lastObject];
128+
[existingSurfaceView removeFromSuperview];
129+
}
130+
131+
#pragma mark - RCTSurfaceDelegate
132+
133+
- (void)surface:(RCTSurface *)surface didChangeIntrinsicSize:(CGSize)intrinsicSize
134+
{
135+
[self setIntrinsicSize:intrinsicSize];
136+
}
137+
138+
- (void)surface:(RCTSurface *)surface didChangeStage:(RCTSurfaceStage)stage
139+
{
140+
[self setStage:stage];
141+
}
142+
143+
@end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
#import <UIKit/UIKit.h>
11+
12+
#import <ComponentKit/CKComponent.h>
13+
14+
typedef CKComponent *(^RCTSurfaceHostingComponentOptionsActivityIndicatorComponentFactory)();
15+
16+
struct RCTSurfaceHostingComponentOptions {
17+
NSTimeInterval synchronousLayoutingTimeout = 0.350;
18+
BOOL synchronousStateUpdates = YES;
19+
CGSize activityIndicatorSize = {44.0, 44.0};
20+
BOOL boundsAnimations = YES;
21+
RCTSurfaceHostingComponentOptionsActivityIndicatorComponentFactory activityIndicatorComponentFactory = nil;
22+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
#import <UIKit/UIKit.h>
11+
12+
#import <React/RCTSurfaceStage.h>
13+
14+
@interface RCTSurfaceHostingComponentState: NSObject
15+
16+
@property (nonatomic, readonly, assign) CGSize intrinsicSize;
17+
@property (nonatomic, readonly, assign) RCTSurfaceStage stage;
18+
19+
+ (instancetype)newWithStage:(RCTSurfaceStage)stage
20+
intrinsicSize:(CGSize)intrinsicSize;
21+
22+
@end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
#import "RCTSurfaceHostingComponentState.h"
11+
12+
@implementation RCTSurfaceHostingComponentState
13+
14+
+ (instancetype)newWithStage:(RCTSurfaceStage)stage
15+
intrinsicSize:(CGSize)intrinsicSize
16+
{
17+
return [[self alloc] initWithStage:stage intrinsicSize:intrinsicSize];
18+
}
19+
20+
21+
- (instancetype)initWithStage:(RCTSurfaceStage)stage
22+
intrinsicSize:(CGSize)intrinsicSize
23+
{
24+
if (self = [super init]) {
25+
_stage = stage;
26+
_intrinsicSize = intrinsicSize;
27+
}
28+
29+
return self;
30+
}
31+
32+
- (BOOL)isEqual:(RCTSurfaceHostingComponentState *)other
33+
{
34+
if (other == self) {
35+
return YES;
36+
}
37+
38+
return
39+
_stage == other->_stage &&
40+
CGSizeEqualToSize(_intrinsicSize, other->_intrinsicSize);
41+
}
42+
43+
@end

0 commit comments

Comments
 (0)