1 | #import "RNSScreenContainer.h"
|
2 | #import "RNSScreen.h"
|
3 |
|
4 | #ifdef RCT_NEW_ARCH_ENABLED
|
5 | #import <React/RCTConversions.h>
|
6 | #import <React/RCTFabricComponentsPlugins.h>
|
7 | #import <react/renderer/components/rnscreens/ComponentDescriptors.h>
|
8 | #import <react/renderer/components/rnscreens/Props.h>
|
9 |
|
10 | namespace react = facebook::react;
|
11 |
|
12 | #endif // RCT_NEW_ARCH_ENABLED
|
13 |
|
14 | @implementation RNSViewController
|
15 |
|
16 | #if !TARGET_OS_TV
|
17 | - (UIViewController *)childViewControllerForStatusBarStyle
|
18 | {
|
19 | return [self findActiveChildVC];
|
20 | }
|
21 |
|
22 | - (UIStatusBarAnimation)preferredStatusBarUpdateAnimation
|
23 | {
|
24 | return [self findActiveChildVC].preferredStatusBarUpdateAnimation;
|
25 | }
|
26 |
|
27 | - (UIViewController *)childViewControllerForStatusBarHidden
|
28 | {
|
29 | return [self findActiveChildVC];
|
30 | }
|
31 |
|
32 | - (UIInterfaceOrientationMask)supportedInterfaceOrientations
|
33 | {
|
34 | return [self findActiveChildVC].supportedInterfaceOrientations;
|
35 | }
|
36 |
|
37 | - (UIViewController *)childViewControllerForHomeIndicatorAutoHidden
|
38 | {
|
39 | return [self findActiveChildVC];
|
40 | }
|
41 | #endif
|
42 |
|
43 | - (UIViewController *)findActiveChildVC
|
44 | {
|
45 | for (UIViewController *childVC in self.childViewControllers) {
|
46 | if ([childVC isKindOfClass:[RNSScreen class]] &&
|
47 | ((RNSScreen *)childVC).screenView.activityState == RNSActivityStateOnTop) {
|
48 | return childVC;
|
49 | }
|
50 | }
|
51 | return [[self childViewControllers] lastObject];
|
52 | }
|
53 |
|
54 | @end
|
55 |
|
56 | @implementation RNSScreenContainerView {
|
57 | BOOL _invalidated;
|
58 | NSMutableSet *_activeScreens;
|
59 | }
|
60 |
|
61 | - (instancetype)init
|
62 | {
|
63 | if (self = [super initWithFrame:CGRectZero]) {
|
64 | #ifdef RCT_NEW_ARCH_ENABLED
|
65 | static const auto defaultProps = std::make_shared<const react::RNSScreenContainerProps>();
|
66 | _props = defaultProps;
|
67 | #endif
|
68 | _activeScreens = [NSMutableSet new];
|
69 | _reactSubviews = [NSMutableArray new];
|
70 | [self setupController];
|
71 | _invalidated = NO;
|
72 | }
|
73 | return self;
|
74 | }
|
75 |
|
76 | - (void)setupController
|
77 | {
|
78 | _controller = [[RNSViewController alloc] init];
|
79 | [self addSubview:_controller.view];
|
80 | }
|
81 |
|
82 | - (void)markChildUpdated
|
83 | {
|
84 |
|
85 |
|
86 |
|
87 |
|
88 | RCTAssertMainQueue();
|
89 | [self updateContainer];
|
90 | }
|
91 |
|
92 | - (void)insertReactSubview:(RNSScreenView *)subview atIndex:(NSInteger)atIndex
|
93 | {
|
94 | subview.reactSuperview = self;
|
95 | [_reactSubviews insertObject:subview atIndex:atIndex];
|
96 | [subview reactSetFrame:CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height)];
|
97 | }
|
98 |
|
99 | - (void)removeReactSubview:(RNSScreenView *)subview
|
100 | {
|
101 | subview.reactSuperview = nil;
|
102 | [_reactSubviews removeObject:subview];
|
103 | }
|
104 |
|
105 | - (NSArray<UIView *> *)reactSubviews
|
106 | {
|
107 | return _reactSubviews;
|
108 | }
|
109 |
|
110 | - (UIViewController *)reactViewController
|
111 | {
|
112 | return _controller;
|
113 | }
|
114 |
|
115 | - (UIViewController *)findChildControllerForScreen:(RNSScreenView *)screen
|
116 | {
|
117 | for (UIViewController *vc in _controller.childViewControllers) {
|
118 | if (vc.view == screen) {
|
119 | return vc;
|
120 | }
|
121 | }
|
122 | return nil;
|
123 | }
|
124 |
|
125 | - (void)prepareDetach:(RNSScreenView *)screen
|
126 | {
|
127 | [[self findChildControllerForScreen:screen] willMoveToParentViewController:nil];
|
128 | }
|
129 |
|
130 | - (void)detachScreen:(RNSScreenView *)screen
|
131 | {
|
132 |
|
133 |
|
134 |
|
135 |
|
136 | UIViewController *detachController = [self findChildControllerForScreen:screen];
|
137 | [detachController willMoveToParentViewController:nil];
|
138 | [screen removeFromSuperview];
|
139 | [detachController removeFromParentViewController];
|
140 | [_activeScreens removeObject:screen];
|
141 | }
|
142 |
|
143 | - (void)attachScreen:(RNSScreenView *)screen atIndex:(NSInteger)index
|
144 | {
|
145 | [_controller addChildViewController:screen.controller];
|
146 |
|
147 |
|
148 |
|
149 | [_controller.view insertSubview:screen.controller.view atIndex:index];
|
150 | [screen.controller didMoveToParentViewController:_controller];
|
151 | [_activeScreens addObject:screen];
|
152 | }
|
153 |
|
154 | - (void)updateContainer
|
155 | {
|
156 | BOOL screenRemoved = NO;
|
157 |
|
158 | NSMutableSet *orphaned = [NSMutableSet setWithSet:_activeScreens];
|
159 | for (RNSScreenView *screen in _reactSubviews) {
|
160 | if (screen.activityState == RNSActivityStateInactive && [_activeScreens containsObject:screen]) {
|
161 | screenRemoved = YES;
|
162 | [self detachScreen:screen];
|
163 | }
|
164 | [orphaned removeObject:screen];
|
165 | }
|
166 | for (RNSScreenView *screen in orphaned) {
|
167 | screenRemoved = YES;
|
168 | [self detachScreen:screen];
|
169 | }
|
170 |
|
171 |
|
172 | BOOL screenAdded = NO;
|
173 | for (RNSScreenView *screen in _reactSubviews) {
|
174 | if (screen.activityState != RNSActivityStateInactive && ![_activeScreens containsObject:screen]) {
|
175 | screenAdded = YES;
|
176 | }
|
177 | }
|
178 |
|
179 | if (screenAdded) {
|
180 |
|
181 | NSInteger index = 0;
|
182 | for (RNSScreenView *screen in _reactSubviews) {
|
183 | if (screen.activityState != RNSActivityStateInactive) {
|
184 | if ([_activeScreens containsObject:screen] && screen.activityState == RNSActivityStateTransitioningOrBelowTop) {
|
185 |
|
186 |
|
187 | [self prepareDetach:screen];
|
188 | } else if (![_activeScreens containsObject:screen]) {
|
189 | [self attachScreen:screen atIndex:index];
|
190 | }
|
191 | index += 1;
|
192 | }
|
193 | }
|
194 | }
|
195 |
|
196 | for (RNSScreenView *screen in _reactSubviews) {
|
197 | if (screen.activityState == RNSActivityStateOnTop) {
|
198 | [screen notifyFinishTransitioning];
|
199 | }
|
200 | }
|
201 |
|
202 | if (screenRemoved || screenAdded) {
|
203 | [self maybeDismissVC];
|
204 | }
|
205 | }
|
206 |
|
207 | - (void)maybeDismissVC
|
208 | {
|
209 | if (_controller.presentedViewController == nil && _controller.presentingViewController == nil) {
|
210 |
|
211 |
|
212 |
|
213 |
|
214 |
|
215 |
|
216 | [_controller dismissViewControllerAnimated:NO completion:nil];
|
217 | }
|
218 | }
|
219 |
|
220 | - (void)didUpdateReactSubviews
|
221 | {
|
222 | [self markChildUpdated];
|
223 | }
|
224 |
|
225 | - (void)didMoveToWindow
|
226 | {
|
227 | if (self.window && !_invalidated) {
|
228 |
|
229 |
|
230 |
|
231 | [self reactAddControllerToClosestParent:_controller];
|
232 | }
|
233 | }
|
234 |
|
235 | - (void)layoutSubviews
|
236 | {
|
237 | [super layoutSubviews];
|
238 | _controller.view.frame = self.bounds;
|
239 | for (RNSScreenView *subview in _reactSubviews) {
|
240 | #ifdef RCT_NEW_ARCH_ENABLED
|
241 | react::LayoutMetrics screenLayoutMetrics = subview.newLayoutMetrics;
|
242 | screenLayoutMetrics.frame = RCTRectFromCGRect(CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height));
|
243 | [subview updateLayoutMetrics:screenLayoutMetrics oldLayoutMetrics:subview.oldLayoutMetrics];
|
244 | #else
|
245 | [subview reactSetFrame:CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height)];
|
246 | #endif
|
247 | [subview setNeedsLayout];
|
248 | }
|
249 | }
|
250 |
|
251 | #pragma mark-- Fabric specific
|
252 | #ifdef RCT_NEW_ARCH_ENABLED
|
253 |
|
254 | - (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
|
255 | {
|
256 | if (![childComponentView isKindOfClass:[RNSScreenView class]]) {
|
257 | RCTLogError(@"ScreenContainer only accepts children of type Screen");
|
258 | return;
|
259 | }
|
260 |
|
261 | RNSScreenView *screenView = (RNSScreenView *)childComponentView;
|
262 |
|
263 | RCTAssert(
|
264 | childComponentView.reactSuperview == nil,
|
265 | @"Attempt to mount already mounted component view. (parent: %@, child: %@, index: %@, existing parent: %@)",
|
266 | self,
|
267 | childComponentView,
|
268 | @(index),
|
269 | @([childComponentView.superview tag]));
|
270 |
|
271 | [_reactSubviews insertObject:screenView atIndex:index];
|
272 | screenView.reactSuperview = self;
|
273 | react::LayoutMetrics screenLayoutMetrics = screenView.newLayoutMetrics;
|
274 | screenLayoutMetrics.frame = RCTRectFromCGRect(CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height));
|
275 | [screenView updateLayoutMetrics:screenLayoutMetrics oldLayoutMetrics:screenView.oldLayoutMetrics];
|
276 | [self markChildUpdated];
|
277 | }
|
278 |
|
279 | - (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
|
280 | {
|
281 | RCTAssert(
|
282 | childComponentView.reactSuperview == self,
|
283 | @"Attempt to unmount a view which is mounted inside different view. (parent: %@, child: %@, index: %@)",
|
284 | self,
|
285 | childComponentView,
|
286 | @(index));
|
287 | RCTAssert(
|
288 | (_reactSubviews.count > index) && [_reactSubviews objectAtIndex:index] == childComponentView,
|
289 | @"Attempt to unmount a view which has a different index. (parent: %@, child: %@, index: %@, actual index: %@, tag at index: %@)",
|
290 | self,
|
291 | childComponentView,
|
292 | @(index),
|
293 | @([_reactSubviews indexOfObject:childComponentView]),
|
294 | @([[_reactSubviews objectAtIndex:index] tag]));
|
295 | ((RNSScreenView *)childComponentView).reactSuperview = nil;
|
296 | [_reactSubviews removeObject:childComponentView];
|
297 | [childComponentView removeFromSuperview];
|
298 | [self markChildUpdated];
|
299 | }
|
300 |
|
301 | + (react::ComponentDescriptorProvider)componentDescriptorProvider
|
302 | {
|
303 | return react::concreteComponentDescriptorProvider<react::RNSScreenContainerComponentDescriptor>();
|
304 | }
|
305 |
|
306 | - (void)prepareForRecycle
|
307 | {
|
308 | [super prepareForRecycle];
|
309 | [_controller willMoveToParentViewController:nil];
|
310 | [_controller removeFromParentViewController];
|
311 | }
|
312 |
|
313 | #pragma mark-- Paper specific
|
314 | #else
|
315 |
|
316 | - (void)invalidate
|
317 | {
|
318 | _invalidated = YES;
|
319 | [_controller willMoveToParentViewController:nil];
|
320 | [_controller removeFromParentViewController];
|
321 | }
|
322 |
|
323 | #endif
|
324 |
|
325 | @end
|
326 |
|
327 | #ifdef RCT_NEW_ARCH_ENABLED
|
328 | Class<RCTComponentViewProtocol> RNSScreenContainerCls(void)
|
329 | {
|
330 | return RNSScreenContainerView.class;
|
331 | }
|
332 | #endif
|
333 |
|
334 | @implementation RNSScreenContainerManager
|
335 |
|
336 | RCT_EXPORT_MODULE()
|
337 |
|
338 | - (UIView *)view
|
339 | {
|
340 | return [[RNSScreenContainerView alloc] init];
|
341 | }
|
342 |
|
343 | @end
|