1 | #import <UIKit/UIKit.h>
|
2 |
|
3 | #import "RNSScreen.h"
|
4 | #import "RNSScreenContainer.h"
|
5 | #import "RNSScreenWindowTraits.h"
|
6 |
|
7 | #ifdef RCT_NEW_ARCH_ENABLED
|
8 | #import <React/RCTConversions.h>
|
9 | #import <React/RCTFabricComponentsPlugins.h>
|
10 | #import <React/RCTRootComponentView.h>
|
11 | #import <React/RCTSurfaceTouchHandler.h>
|
12 | #import <react/renderer/components/rnscreens/EventEmitters.h>
|
13 | #import <react/renderer/components/rnscreens/Props.h>
|
14 | #import <react/renderer/components/rnscreens/RCTComponentViewHelpers.h>
|
15 | #import <rnscreens/RNSScreenComponentDescriptor.h>
|
16 | #import "RNSConvert.h"
|
17 | #import "RNSHeaderHeightChangeEvent.h"
|
18 | #import "RNSScreenViewEvent.h"
|
19 | #else
|
20 | #import <React/RCTTouchHandler.h>
|
21 | #endif
|
22 |
|
23 | #import <React/RCTShadowView.h>
|
24 | #import <React/RCTUIManager.h>
|
25 | #import "RNSScreenStack.h"
|
26 | #import "RNSScreenStackHeaderConfig.h"
|
27 |
|
28 | #ifdef RCT_NEW_ARCH_ENABLED
|
29 | namespace react = facebook::react;
|
30 | #endif // RCT_NEW_ARCH_ENABLED
|
31 |
|
32 | @interface RNSScreenView ()
|
33 | #ifdef RCT_NEW_ARCH_ENABLED
|
34 | <RCTRNSScreenViewProtocol, UIAdaptivePresentationControllerDelegate>
|
35 | #else
|
36 | <UIAdaptivePresentationControllerDelegate, RCTInvalidating>
|
37 | #endif
|
38 | @end
|
39 |
|
40 | @implementation RNSScreenView {
|
41 | #ifdef RCT_NEW_ARCH_ENABLED
|
42 | RCTSurfaceTouchHandler *_touchHandler;
|
43 | react::RNSScreenShadowNode::ConcreteState::Shared _state;
|
44 |
|
45 | NSMutableArray<UIView *> *_reactSubviews;
|
46 | #else
|
47 | __weak RCTBridge *_bridge;
|
48 | RCTTouchHandler *_touchHandler;
|
49 | CGRect _reactFrame;
|
50 | #endif
|
51 | }
|
52 |
|
53 | #ifdef RCT_NEW_ARCH_ENABLED
|
54 | - (instancetype)initWithFrame:(CGRect)frame
|
55 | {
|
56 | if (self = [super initWithFrame:frame]) {
|
57 | static const auto defaultProps = std::make_shared<const react::RNSScreenProps>();
|
58 | _props = defaultProps;
|
59 | _reactSubviews = [NSMutableArray new];
|
60 | [self initCommonProps];
|
61 | }
|
62 | return self;
|
63 | }
|
64 | #else
|
65 | - (instancetype)initWithBridge:(RCTBridge *)bridge
|
66 | {
|
67 | if (self = [super init]) {
|
68 | _bridge = bridge;
|
69 | [self initCommonProps];
|
70 | }
|
71 |
|
72 | return self;
|
73 | }
|
74 | #endif // RCT_NEW_ARCH_ENABLED
|
75 |
|
76 | - (void)initCommonProps
|
77 | {
|
78 | _controller = [[RNSScreen alloc] initWithView:self];
|
79 | _stackPresentation = RNSScreenStackPresentationPush;
|
80 | _stackAnimation = RNSScreenStackAnimationDefault;
|
81 | _gestureEnabled = YES;
|
82 | _replaceAnimation = RNSScreenReplaceAnimationPop;
|
83 | _dismissed = NO;
|
84 | _hasStatusBarStyleSet = NO;
|
85 | _hasStatusBarAnimationSet = NO;
|
86 | _hasStatusBarHiddenSet = NO;
|
87 | _hasOrientationSet = NO;
|
88 | _hasHomeIndicatorHiddenSet = NO;
|
89 | #if !TARGET_OS_TV
|
90 | _sheetExpandsWhenScrolledToEdge = YES;
|
91 | #endif // !TARGET_OS_TV
|
92 | }
|
93 |
|
94 | - (UIViewController *)reactViewController
|
95 | {
|
96 | return _controller;
|
97 | }
|
98 |
|
99 | #ifdef RCT_NEW_ARCH_ENABLED
|
100 | - (NSArray<UIView *> *)reactSubviews
|
101 | {
|
102 | return _reactSubviews;
|
103 | }
|
104 | #endif
|
105 |
|
106 | - (void)updateBounds
|
107 | {
|
108 | #ifdef RCT_NEW_ARCH_ENABLED
|
109 | if (_state != nullptr) {
|
110 | RNSScreenStackHeaderConfig *config = [self findHeaderConfig];
|
111 |
|
112 | CGFloat headerHeight =
|
113 | config.largeTitle ? 0 : [_controller calculateHeaderHeightIsModal:self.isPresentedAsNativeModal];
|
114 | auto newState =
|
115 | react::RNSScreenState{RCTSizeFromCGSize(self.bounds.size), RCTPointFromCGPoint(CGPointMake(0, headerHeight))};
|
116 | _state->updateState(std::move(newState));
|
117 | UINavigationController *navctr = _controller.navigationController;
|
118 | [navctr.view setNeedsLayout];
|
119 | }
|
120 | #else
|
121 | [_bridge.uiManager setSize:self.bounds.size forView:self];
|
122 | #endif
|
123 | }
|
124 |
|
125 | - (void)setStackPresentation:(RNSScreenStackPresentation)stackPresentation
|
126 | {
|
127 | switch (stackPresentation) {
|
128 | case RNSScreenStackPresentationModal:
|
129 | #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \
|
130 | __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
|
131 | if (@available(iOS 13.0, tvOS 13.0, *)) {
|
132 | _controller.modalPresentationStyle = UIModalPresentationAutomatic;
|
133 | } else {
|
134 | _controller.modalPresentationStyle = UIModalPresentationFullScreen;
|
135 | }
|
136 | #else
|
137 | _controller.modalPresentationStyle = UIModalPresentationFullScreen;
|
138 | #endif
|
139 | break;
|
140 | case RNSScreenStackPresentationFullScreenModal:
|
141 | _controller.modalPresentationStyle = UIModalPresentationFullScreen;
|
142 | break;
|
143 | #if !TARGET_OS_TV
|
144 | case RNSScreenStackPresentationFormSheet:
|
145 | _controller.modalPresentationStyle = UIModalPresentationFormSheet;
|
146 | break;
|
147 | #endif
|
148 | case RNSScreenStackPresentationTransparentModal:
|
149 | _controller.modalPresentationStyle = UIModalPresentationOverFullScreen;
|
150 | break;
|
151 | case RNSScreenStackPresentationContainedModal:
|
152 | _controller.modalPresentationStyle = UIModalPresentationCurrentContext;
|
153 | break;
|
154 | case RNSScreenStackPresentationContainedTransparentModal:
|
155 | _controller.modalPresentationStyle = UIModalPresentationOverCurrentContext;
|
156 | break;
|
157 | case RNSScreenStackPresentationPush:
|
158 |
|
159 | break;
|
160 | }
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 | if (stackPresentation != RNSScreenStackPresentationPush) {
|
167 |
|
168 |
|
169 |
|
170 |
|
171 | _controller.presentationController.delegate = self;
|
172 | } else if (_stackPresentation != RNSScreenStackPresentationPush) {
|
173 | #ifdef RCT_NEW_ARCH_ENABLED
|
174 | #else
|
175 | RCTLogError(
|
176 | @"Screen presentation updated from modal to push, this may likely result in a screen object leakage. If you need to change presentation style create a new screen object instead");
|
177 | #endif // RCT_NEW_ARCH_ENABLED
|
178 | }
|
179 | _stackPresentation = stackPresentation;
|
180 | }
|
181 |
|
182 | - (void)setStackAnimation:(RNSScreenStackAnimation)stackAnimation
|
183 | {
|
184 | _stackAnimation = stackAnimation;
|
185 |
|
186 | switch (stackAnimation) {
|
187 | case RNSScreenStackAnimationFade:
|
188 | _controller.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
|
189 | break;
|
190 | #if !TARGET_OS_TV
|
191 | case RNSScreenStackAnimationFlip:
|
192 | _controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
|
193 | break;
|
194 | #endif
|
195 | case RNSScreenStackAnimationNone:
|
196 | case RNSScreenStackAnimationDefault:
|
197 | case RNSScreenStackAnimationSimplePush:
|
198 | case RNSScreenStackAnimationSlideFromBottom:
|
199 | case RNSScreenStackAnimationFadeFromBottom:
|
200 | case RNSScreenStackAnimationSlideFromLeft:
|
201 |
|
202 | break;
|
203 | }
|
204 | }
|
205 |
|
206 | - (void)setGestureEnabled:(BOOL)gestureEnabled
|
207 | {
|
208 | #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \
|
209 | __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
|
210 | if (@available(iOS 13.0, tvOS 13.0, *)) {
|
211 | _controller.modalInPresentation = !gestureEnabled;
|
212 | }
|
213 | #endif
|
214 |
|
215 | _gestureEnabled = gestureEnabled;
|
216 | }
|
217 |
|
218 | - (void)setReplaceAnimation:(RNSScreenReplaceAnimation)replaceAnimation
|
219 | {
|
220 | _replaceAnimation = replaceAnimation;
|
221 | }
|
222 |
|
223 |
|
224 |
|
225 |
|
226 |
|
227 | - (void)setActivityStateOrNil:(NSNumber *)activityStateOrNil
|
228 | {
|
229 | int activityState = [activityStateOrNil intValue];
|
230 | if (activityStateOrNil != nil && activityState != -1 && activityState != _activityState) {
|
231 | _activityState = activityState;
|
232 | [_reactSuperview markChildUpdated];
|
233 | }
|
234 | }
|
235 |
|
236 | #if !TARGET_OS_TV && !TARGET_OS_VISION
|
237 | - (void)setStatusBarStyle:(RNSStatusBarStyle)statusBarStyle
|
238 | {
|
239 | _hasStatusBarStyleSet = YES;
|
240 | _statusBarStyle = statusBarStyle;
|
241 | [RNSScreenWindowTraits assertViewControllerBasedStatusBarAppearenceSet];
|
242 | [RNSScreenWindowTraits updateStatusBarAppearance];
|
243 | }
|
244 |
|
245 | - (void)setStatusBarAnimation:(UIStatusBarAnimation)statusBarAnimation
|
246 | {
|
247 | _hasStatusBarAnimationSet = YES;
|
248 | _statusBarAnimation = statusBarAnimation;
|
249 | [RNSScreenWindowTraits assertViewControllerBasedStatusBarAppearenceSet];
|
250 | }
|
251 |
|
252 | - (void)setStatusBarHidden:(BOOL)statusBarHidden
|
253 | {
|
254 | _hasStatusBarHiddenSet = YES;
|
255 | _statusBarHidden = statusBarHidden;
|
256 | [RNSScreenWindowTraits assertViewControllerBasedStatusBarAppearenceSet];
|
257 | [RNSScreenWindowTraits updateStatusBarAppearance];
|
258 |
|
259 |
|
260 |
|
261 |
|
262 | if (self.controller.navigationController.navigationBarHidden && !self.isModal) {
|
263 | [self.controller calculateAndNotifyHeaderHeightChangeIsModal:NO];
|
264 | }
|
265 | }
|
266 |
|
267 | - (void)setScreenOrientation:(UIInterfaceOrientationMask)screenOrientation
|
268 | {
|
269 | _hasOrientationSet = YES;
|
270 | _screenOrientation = screenOrientation;
|
271 | [RNSScreenWindowTraits enforceDesiredDeviceOrientation];
|
272 | }
|
273 |
|
274 | - (void)setHomeIndicatorHidden:(BOOL)homeIndicatorHidden
|
275 | {
|
276 | _hasHomeIndicatorHiddenSet = YES;
|
277 | _homeIndicatorHidden = homeIndicatorHidden;
|
278 | [RNSScreenWindowTraits updateHomeIndicatorAutoHidden];
|
279 | }
|
280 | #endif
|
281 |
|
282 | - (UIView *)reactSuperview
|
283 | {
|
284 | return _reactSuperview;
|
285 | }
|
286 |
|
287 | - (void)addSubview:(UIView *)view
|
288 | {
|
289 | if (![view isKindOfClass:[RNSScreenStackHeaderConfig class]]) {
|
290 | [super addSubview:view];
|
291 | } else {
|
292 | ((RNSScreenStackHeaderConfig *)view).screenView = self;
|
293 | }
|
294 | }
|
295 |
|
296 | - (void)notifyDismissedWithCount:(int)dismissCount
|
297 | {
|
298 | #ifdef RCT_NEW_ARCH_ENABLED
|
299 |
|
300 | if (_eventEmitter != nullptr) {
|
301 | std::dynamic_pointer_cast<const react::RNSScreenEventEmitter>(_eventEmitter)
|
302 | ->onDismissed(react::RNSScreenEventEmitter::OnDismissed{.dismissCount = dismissCount});
|
303 | }
|
304 | #else
|
305 |
|
306 | _dismissed = YES;
|
307 | if (self.onDismissed) {
|
308 | dispatch_async(dispatch_get_main_queue(), ^{
|
309 | if (self.onDismissed) {
|
310 | self.onDismissed(@{@"dismissCount" : @(dismissCount)});
|
311 | }
|
312 | });
|
313 | }
|
314 | #endif
|
315 | }
|
316 |
|
317 | - (void)notifyDismissCancelledWithDismissCount:(int)dismissCount
|
318 | {
|
319 | #ifdef RCT_NEW_ARCH_ENABLED
|
320 |
|
321 | if (_eventEmitter != nullptr) {
|
322 | std::dynamic_pointer_cast<const react::RNSScreenEventEmitter>(_eventEmitter)
|
323 | ->onNativeDismissCancelled(
|
324 | react::RNSScreenEventEmitter::OnNativeDismissCancelled{.dismissCount = dismissCount});
|
325 | }
|
326 | #else
|
327 | if (self.onNativeDismissCancelled) {
|
328 | self.onNativeDismissCancelled(@{@"dismissCount" : @(dismissCount)});
|
329 | }
|
330 | #endif
|
331 | }
|
332 |
|
333 | - (void)notifyWillAppear
|
334 | {
|
335 | #ifdef RCT_NEW_ARCH_ENABLED
|
336 |
|
337 | if (_eventEmitter != nullptr) {
|
338 | std::dynamic_pointer_cast<const react::RNSScreenEventEmitter>(_eventEmitter)
|
339 | ->onWillAppear(react::RNSScreenEventEmitter::OnWillAppear{});
|
340 | }
|
341 | [self updateLayoutMetrics:_newLayoutMetrics oldLayoutMetrics:_oldLayoutMetrics];
|
342 | #else
|
343 | if (self.onWillAppear) {
|
344 | self.onWillAppear(nil);
|
345 | }
|
346 |
|
347 |
|
348 | [self reactSetFrame:_reactFrame];
|
349 | #endif
|
350 | }
|
351 |
|
352 | - (void)notifyWillDisappear
|
353 | {
|
354 | if (_hideKeyboardOnSwipe) {
|
355 | [self endEditing:YES];
|
356 | }
|
357 | #ifdef RCT_NEW_ARCH_ENABLED
|
358 |
|
359 | if (_eventEmitter != nullptr) {
|
360 | std::dynamic_pointer_cast<const react::RNSScreenEventEmitter>(_eventEmitter)
|
361 | ->onWillDisappear(react::RNSScreenEventEmitter::OnWillDisappear{});
|
362 | }
|
363 | #else
|
364 | if (self.onWillDisappear) {
|
365 | self.onWillDisappear(nil);
|
366 | }
|
367 | #endif
|
368 | }
|
369 |
|
370 | - (void)notifyAppear
|
371 | {
|
372 | #ifdef RCT_NEW_ARCH_ENABLED
|
373 |
|
374 | if (_eventEmitter != nullptr) {
|
375 | std::dynamic_pointer_cast<const react::RNSScreenEventEmitter>(_eventEmitter)
|
376 | ->onAppear(react::RNSScreenEventEmitter::OnAppear{});
|
377 | }
|
378 | #else
|
379 | if (self.onAppear) {
|
380 | dispatch_async(dispatch_get_main_queue(), ^{
|
381 | if (self.onAppear) {
|
382 | self.onAppear(nil);
|
383 | }
|
384 | });
|
385 | }
|
386 | #endif
|
387 | }
|
388 |
|
389 | - (void)notifyDisappear
|
390 | {
|
391 | #ifdef RCT_NEW_ARCH_ENABLED
|
392 |
|
393 | if (_eventEmitter != nullptr) {
|
394 | std::dynamic_pointer_cast<const react::RNSScreenEventEmitter>(_eventEmitter)
|
395 | ->onDisappear(react::RNSScreenEventEmitter::OnDisappear{});
|
396 | }
|
397 | #else
|
398 | if (self.onDisappear) {
|
399 | self.onDisappear(nil);
|
400 | }
|
401 | #endif
|
402 | }
|
403 |
|
404 | - (void)notifyHeaderHeightChange:(double)headerHeight
|
405 | {
|
406 | #ifdef RCT_NEW_ARCH_ENABLED
|
407 | if (_eventEmitter != nullptr) {
|
408 | std::dynamic_pointer_cast<const facebook::react::RNSScreenEventEmitter>(_eventEmitter)
|
409 | ->onHeaderHeightChange(
|
410 | facebook::react::RNSScreenEventEmitter::OnHeaderHeightChange{.headerHeight = headerHeight});
|
411 | }
|
412 |
|
413 | RNSHeaderHeightChangeEvent *event =
|
414 | [[RNSHeaderHeightChangeEvent alloc] initWithEventName:@"onHeaderHeightChange"
|
415 | reactTag:[NSNumber numberWithInt:self.tag]
|
416 | headerHeight:headerHeight];
|
417 | [[RCTBridge currentBridge].eventDispatcher notifyObserversOfEvent:event];
|
418 | #else
|
419 | if (self.onHeaderHeightChange) {
|
420 | self.onHeaderHeightChange(@{
|
421 | @"headerHeight" : @(headerHeight),
|
422 | });
|
423 | }
|
424 | #endif
|
425 | }
|
426 |
|
427 | - (void)notifyGestureCancel
|
428 | {
|
429 | #ifdef RCT_NEW_ARCH_ENABLED
|
430 | if (_eventEmitter != nullptr) {
|
431 | std::dynamic_pointer_cast<const react::RNSScreenEventEmitter>(_eventEmitter)
|
432 | ->onGestureCancel(react::RNSScreenEventEmitter::OnGestureCancel{});
|
433 | }
|
434 | #else
|
435 | if (self.onGestureCancel) {
|
436 | self.onGestureCancel(nil);
|
437 | }
|
438 | #endif
|
439 | }
|
440 |
|
441 | - (BOOL)isMountedUnderScreenOrReactRoot
|
442 | {
|
443 | #ifdef RCT_NEW_ARCH_ENABLED
|
444 | #define RNS_EXPECTED_VIEW RCTRootComponentView
|
445 | #else
|
446 | #define RNS_EXPECTED_VIEW RCTRootView
|
447 | #endif
|
448 | for (UIView *parent = self.superview; parent != nil; parent = parent.superview) {
|
449 | if ([parent isKindOfClass:[RNS_EXPECTED_VIEW class]] || [parent isKindOfClass:[RNSScreenView class]]) {
|
450 | return YES;
|
451 | }
|
452 | }
|
453 | return NO;
|
454 | #undef RNS_EXPECTED_VIEW
|
455 | }
|
456 |
|
457 | - (void)didMoveToWindow
|
458 | {
|
459 |
|
460 |
|
461 |
|
462 | if (self.window != nil && ![self isMountedUnderScreenOrReactRoot]) {
|
463 | if (_touchHandler == nil) {
|
464 | #ifdef RCT_NEW_ARCH_ENABLED
|
465 | _touchHandler = [RCTSurfaceTouchHandler new];
|
466 | #else
|
467 | _touchHandler = [[RCTTouchHandler alloc] initWithBridge:_bridge];
|
468 | #endif
|
469 | }
|
470 | [_touchHandler attachToView:self];
|
471 | } else {
|
472 | [_touchHandler detachFromView:self];
|
473 | }
|
474 | }
|
475 |
|
476 | #ifdef RCT_NEW_ARCH_ENABLED
|
477 | - (RCTSurfaceTouchHandler *)touchHandler
|
478 | #else
|
479 | - (RCTTouchHandler *)touchHandler
|
480 | #endif
|
481 | {
|
482 | if (_touchHandler != nil) {
|
483 | return _touchHandler;
|
484 | }
|
485 | UIView *parent = [self superview];
|
486 | while (parent != nil && ![parent respondsToSelector:@selector(touchHandler)])
|
487 | parent = parent.superview;
|
488 | if (parent != nil) {
|
489 | return [parent performSelector:@selector(touchHandler)];
|
490 | }
|
491 | return nil;
|
492 | }
|
493 |
|
494 | - (void)notifyFinishTransitioning
|
495 | {
|
496 | [_controller notifyFinishTransitioning];
|
497 | }
|
498 |
|
499 | - (void)notifyTransitionProgress:(double)progress closing:(BOOL)closing goingForward:(BOOL)goingForward
|
500 | {
|
501 | #ifdef RCT_NEW_ARCH_ENABLED
|
502 | if (_eventEmitter != nullptr) {
|
503 | std::dynamic_pointer_cast<const react::RNSScreenEventEmitter>(_eventEmitter)
|
504 | ->onTransitionProgress(react::RNSScreenEventEmitter::OnTransitionProgress{
|
505 | .progress = progress, .closing = closing ? 1 : 0, .goingForward = goingForward ? 1 : 0});
|
506 | }
|
507 | RNSScreenViewEvent *event = [[RNSScreenViewEvent alloc] initWithEventName:@"onTransitionProgress"
|
508 | reactTag:[NSNumber numberWithInt:self.tag]
|
509 | progress:progress
|
510 | closing:closing
|
511 | goingForward:goingForward];
|
512 | [[RCTBridge currentBridge].eventDispatcher notifyObserversOfEvent:event];
|
513 | #else
|
514 | if (self.onTransitionProgress) {
|
515 | self.onTransitionProgress(@{
|
516 | @"progress" : @(progress),
|
517 | @"closing" : @(closing ? 1 : 0),
|
518 | @"goingForward" : @(goingForward ? 1 : 0),
|
519 | });
|
520 | }
|
521 | #endif
|
522 | }
|
523 |
|
524 | #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \
|
525 | __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
|
526 | - (void)presentationControllerDidAttemptToDismiss:(UIPresentationController *)presentationController
|
527 | {
|
528 | [self notifyGestureCancel];
|
529 | }
|
530 | #endif
|
531 |
|
532 | - (void)presentationControllerWillDismiss:(UIPresentationController *)presentationController
|
533 | {
|
534 |
|
535 |
|
536 |
|
537 |
|
538 |
|
539 |
|
540 |
|
541 |
|
542 |
|
543 |
|
544 | #ifdef RCT_NEW_ARCH_ENABLED
|
545 | [_touchHandler setEnabled:NO];
|
546 | [_touchHandler setEnabled:YES];
|
547 | #else
|
548 | [_touchHandler cancel];
|
549 | #endif
|
550 | [_touchHandler reset];
|
551 | }
|
552 |
|
553 | - (BOOL)presentationControllerShouldDismiss:(UIPresentationController *)presentationController
|
554 | {
|
555 | if (_preventNativeDismiss) {
|
556 | [self notifyDismissCancelledWithDismissCount:1];
|
557 | return NO;
|
558 | }
|
559 | return _gestureEnabled;
|
560 | }
|
561 |
|
562 | - (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController
|
563 | {
|
564 | if ([_reactSuperview respondsToSelector:@selector(presentationControllerDidDismiss:)]) {
|
565 | [_reactSuperview performSelector:@selector(presentationControllerDidDismiss:) withObject:presentationController];
|
566 | }
|
567 | }
|
568 |
|
569 | - (nullable RNSScreenStackHeaderConfig *)findHeaderConfig
|
570 | {
|
571 |
|
572 | if ([self.reactSubviews.lastObject isKindOfClass:RNSScreenStackHeaderConfig.class]) {
|
573 | return (RNSScreenStackHeaderConfig *)self.reactSubviews.lastObject;
|
574 | }
|
575 |
|
576 | for (UIView *view in self.reactSubviews) {
|
577 | if ([view isKindOfClass:RNSScreenStackHeaderConfig.class]) {
|
578 | return (RNSScreenStackHeaderConfig *)view;
|
579 | }
|
580 | }
|
581 |
|
582 | return nil;
|
583 | }
|
584 |
|
585 | - (BOOL)isModal
|
586 | {
|
587 | return self.stackPresentation != RNSScreenStackPresentationPush;
|
588 | }
|
589 |
|
590 | - (BOOL)isPresentedAsNativeModal
|
591 | {
|
592 | return self.controller.parentViewController == nil && self.controller.presentingViewController != nil;
|
593 | }
|
594 |
|
595 | - (BOOL)isFullscreenModal
|
596 | {
|
597 | switch (self.controller.modalPresentationStyle) {
|
598 | case UIModalPresentationFullScreen:
|
599 | case UIModalPresentationCurrentContext:
|
600 | case UIModalPresentationOverCurrentContext:
|
601 | return YES;
|
602 | default:
|
603 | return NO;
|
604 | }
|
605 | }
|
606 |
|
607 | - (BOOL)isTransparentModal
|
608 | {
|
609 | return self.controller.modalPresentationStyle == UIModalPresentationOverFullScreen ||
|
610 | self.controller.modalPresentationStyle == UIModalPresentationOverCurrentContext;
|
611 | }
|
612 |
|
613 | #if !TARGET_OS_TV && !TARGET_OS_VISION
|
614 |
|
615 |
|
616 |
|
617 |
|
618 |
|
619 | - (void)updatePresentationStyle
|
620 | {
|
621 | #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_15_0) && \
|
622 | __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_15_0
|
623 | if (@available(iOS 15.0, *)) {
|
624 | UISheetPresentationController *sheet = _controller.sheetPresentationController;
|
625 | if (_stackPresentation == RNSScreenStackPresentationFormSheet && sheet != nil) {
|
626 | sheet.prefersScrollingExpandsWhenScrolledToEdge = _sheetExpandsWhenScrolledToEdge;
|
627 | sheet.prefersGrabberVisible = _sheetGrabberVisible;
|
628 | sheet.preferredCornerRadius =
|
629 | _sheetCornerRadius < 0 ? UISheetPresentationControllerAutomaticDimension : _sheetCornerRadius;
|
630 |
|
631 | if (_sheetLargestUndimmedDetent == RNSScreenDetentTypeMedium) {
|
632 | sheet.largestUndimmedDetentIdentifier = UISheetPresentationControllerDetentIdentifierMedium;
|
633 | } else if (_sheetLargestUndimmedDetent == RNSScreenDetentTypeLarge) {
|
634 | sheet.largestUndimmedDetentIdentifier = UISheetPresentationControllerDetentIdentifierLarge;
|
635 | } else if (_sheetLargestUndimmedDetent == RNSScreenDetentTypeAll) {
|
636 | sheet.largestUndimmedDetentIdentifier = nil;
|
637 | } else {
|
638 | RCTLogError(@"Unhandled value of sheetLargestUndimmedDetent passed");
|
639 | }
|
640 |
|
641 | if (_sheetAllowedDetents == RNSScreenDetentTypeMedium) {
|
642 | sheet.detents = @[ UISheetPresentationControllerDetent.mediumDetent ];
|
643 | if (sheet.selectedDetentIdentifier != UISheetPresentationControllerDetentIdentifierMedium) {
|
644 | [sheet animateChanges:^{
|
645 | sheet.selectedDetentIdentifier = UISheetPresentationControllerDetentIdentifierMedium;
|
646 | }];
|
647 | }
|
648 | } else if (_sheetAllowedDetents == RNSScreenDetentTypeLarge) {
|
649 | sheet.detents = @[ UISheetPresentationControllerDetent.largeDetent ];
|
650 | if (sheet.selectedDetentIdentifier != UISheetPresentationControllerDetentIdentifierLarge) {
|
651 | [sheet animateChanges:^{
|
652 | sheet.selectedDetentIdentifier = UISheetPresentationControllerDetentIdentifierLarge;
|
653 | }];
|
654 | }
|
655 | } else if (_sheetAllowedDetents == RNSScreenDetentTypeAll) {
|
656 | sheet.detents =
|
657 | @[ UISheetPresentationControllerDetent.mediumDetent, UISheetPresentationControllerDetent.largeDetent ];
|
658 | } else {
|
659 | RCTLogError(@"Unhandled value of sheetAllowedDetents passed");
|
660 | }
|
661 | }
|
662 | }
|
663 | #endif // Check for max allowed iOS version
|
664 | }
|
665 | #endif // !TARGET_OS_TV
|
666 |
|
667 | #pragma mark - Fabric specific
|
668 | #ifdef RCT_NEW_ARCH_ENABLED
|
669 |
|
670 | - (BOOL)hasHeaderConfig
|
671 | {
|
672 | return _config != nil;
|
673 | }
|
674 |
|
675 | + (react::ComponentDescriptorProvider)componentDescriptorProvider
|
676 | {
|
677 | return react::concreteComponentDescriptorProvider<react::RNSScreenComponentDescriptor>();
|
678 | }
|
679 |
|
680 | + (BOOL)shouldBeRecycled
|
681 | {
|
682 | return NO;
|
683 | }
|
684 |
|
685 | - (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
|
686 | {
|
687 | if ([childComponentView isKindOfClass:[RNSScreenStackHeaderConfig class]]) {
|
688 | _config = (RNSScreenStackHeaderConfig *)childComponentView;
|
689 | _config.screenView = self;
|
690 | }
|
691 | [_reactSubviews insertObject:childComponentView atIndex:index];
|
692 | [super mountChildComponentView:childComponentView index:index];
|
693 | }
|
694 |
|
695 | - (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
|
696 | {
|
697 | if ([childComponentView isKindOfClass:[RNSScreenStackHeaderConfig class]]) {
|
698 | _config = nil;
|
699 | }
|
700 | [_reactSubviews removeObject:childComponentView];
|
701 | [super unmountChildComponentView:childComponentView index:index];
|
702 | }
|
703 |
|
704 | #pragma mark - RCTComponentViewProtocol
|
705 |
|
706 | - (void)updateProps:(react::Props::Shared const &)props oldProps:(react::Props::Shared const &)oldProps
|
707 | {
|
708 | const auto &oldScreenProps = *std::static_pointer_cast<const react::RNSScreenProps>(_props);
|
709 | const auto &newScreenProps = *std::static_pointer_cast<const react::RNSScreenProps>(props);
|
710 |
|
711 | [self setFullScreenSwipeEnabled:newScreenProps.fullScreenSwipeEnabled];
|
712 |
|
713 | [self setGestureEnabled:newScreenProps.gestureEnabled];
|
714 |
|
715 | [self setTransitionDuration:[NSNumber numberWithInt:newScreenProps.transitionDuration]];
|
716 |
|
717 | [self setHideKeyboardOnSwipe:newScreenProps.hideKeyboardOnSwipe];
|
718 |
|
719 | [self setCustomAnimationOnSwipe:newScreenProps.customAnimationOnSwipe];
|
720 |
|
721 | [self
|
722 | setGestureResponseDistance:[RNSConvert
|
723 | gestureResponseDistanceDictFromCppStruct:newScreenProps.gestureResponseDistance]];
|
724 |
|
725 | [self setPreventNativeDismiss:newScreenProps.preventNativeDismiss];
|
726 |
|
727 | [self setActivityStateOrNil:[NSNumber numberWithFloat:newScreenProps.activityState]];
|
728 |
|
729 | [self setSwipeDirection:[RNSConvert RNSScreenSwipeDirectionFromCppEquivalent:newScreenProps.swipeDirection]];
|
730 |
|
731 | #if !TARGET_OS_TV
|
732 | if (newScreenProps.statusBarHidden != oldScreenProps.statusBarHidden) {
|
733 | [self setStatusBarHidden:newScreenProps.statusBarHidden];
|
734 | }
|
735 |
|
736 | if (newScreenProps.statusBarStyle != oldScreenProps.statusBarStyle) {
|
737 | [self setStatusBarStyle:[RCTConvert
|
738 | RNSStatusBarStyle:RCTNSStringFromStringNilIfEmpty(newScreenProps.statusBarStyle)]];
|
739 | }
|
740 |
|
741 | if (newScreenProps.statusBarAnimation != oldScreenProps.statusBarAnimation) {
|
742 | [self setStatusBarAnimation:[RCTConvert UIStatusBarAnimation:RCTNSStringFromStringNilIfEmpty(
|
743 | newScreenProps.statusBarAnimation)]];
|
744 | }
|
745 |
|
746 | if (newScreenProps.screenOrientation != oldScreenProps.screenOrientation) {
|
747 | [self setScreenOrientation:[RCTConvert UIInterfaceOrientationMask:RCTNSStringFromStringNilIfEmpty(
|
748 | newScreenProps.screenOrientation)]];
|
749 | }
|
750 |
|
751 | if (newScreenProps.homeIndicatorHidden != oldScreenProps.homeIndicatorHidden) {
|
752 | [self setHomeIndicatorHidden:newScreenProps.homeIndicatorHidden];
|
753 | }
|
754 |
|
755 | [self setSheetGrabberVisible:newScreenProps.sheetGrabberVisible];
|
756 | [self setSheetCornerRadius:newScreenProps.sheetCornerRadius];
|
757 | [self setSheetExpandsWhenScrolledToEdge:newScreenProps.sheetExpandsWhenScrolledToEdge];
|
758 |
|
759 | if (newScreenProps.sheetAllowedDetents != oldScreenProps.sheetAllowedDetents) {
|
760 | [self setSheetAllowedDetents:[RNSConvert RNSScreenDetentTypeFromAllowedDetents:newScreenProps.sheetAllowedDetents]];
|
761 | }
|
762 |
|
763 | if (newScreenProps.sheetLargestUndimmedDetent != oldScreenProps.sheetLargestUndimmedDetent) {
|
764 | [self setSheetLargestUndimmedDetent:
|
765 | [RNSConvert RNSScreenDetentTypeFromLargestUndimmedDetent:newScreenProps.sheetLargestUndimmedDetent]];
|
766 | }
|
767 | #endif // !TARGET_OS_TV
|
768 |
|
769 | if (newScreenProps.stackPresentation != oldScreenProps.stackPresentation) {
|
770 | [self
|
771 | setStackPresentation:[RNSConvert RNSScreenStackPresentationFromCppEquivalent:newScreenProps.stackPresentation]];
|
772 | }
|
773 |
|
774 | if (newScreenProps.stackAnimation != oldScreenProps.stackAnimation) {
|
775 | [self setStackAnimation:[RNSConvert RNSScreenStackAnimationFromCppEquivalent:newScreenProps.stackAnimation]];
|
776 | }
|
777 |
|
778 | if (newScreenProps.replaceAnimation != oldScreenProps.replaceAnimation) {
|
779 | [self setReplaceAnimation:[RNSConvert RNSScreenReplaceAnimationFromCppEquivalent:newScreenProps.replaceAnimation]];
|
780 | }
|
781 |
|
782 | [super updateProps:props oldProps:oldProps];
|
783 | }
|
784 |
|
785 | - (void)updateState:(react::State::Shared const &)state oldState:(react::State::Shared const &)oldState
|
786 | {
|
787 | _state = std::static_pointer_cast<const react::RNSScreenShadowNode::ConcreteState>(state);
|
788 | }
|
789 |
|
790 | - (void)updateLayoutMetrics:(const react::LayoutMetrics &)layoutMetrics
|
791 | oldLayoutMetrics:(const react::LayoutMetrics &)oldLayoutMetrics
|
792 | {
|
793 | _newLayoutMetrics = layoutMetrics;
|
794 | _oldLayoutMetrics = oldLayoutMetrics;
|
795 | UIViewController *parentVC = self.reactViewController.parentViewController;
|
796 | if (parentVC != nil && ![parentVC isKindOfClass:[RNSNavigationController class]]) {
|
797 | [super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
|
798 | }
|
799 |
|
800 |
|
801 |
|
802 |
|
803 |
|
804 |
|
805 |
|
806 | }
|
807 |
|
808 | - (void)finalizeUpdates:(RNComponentViewUpdateMask)updateMask
|
809 | {
|
810 | [super finalizeUpdates:updateMask];
|
811 | #if !TARGET_OS_TV && !TARGET_OS_VISION
|
812 | [self updatePresentationStyle];
|
813 | #endif // !TARGET_OS_TV
|
814 | }
|
815 |
|
816 | #pragma mark - Paper specific
|
817 | #else
|
818 |
|
819 | - (void)didSetProps:(NSArray<NSString *> *)changedProps
|
820 | {
|
821 | [super didSetProps:changedProps];
|
822 | #if !TARGET_OS_TV && !TARGET_OS_VISION
|
823 | [self updatePresentationStyle];
|
824 | #endif // !TARGET_OS_TV
|
825 | }
|
826 |
|
827 | - (void)setPointerEvents:(RCTPointerEvents)pointerEvents
|
828 | {
|
829 |
|
830 |
|
831 | }
|
832 |
|
833 | - (void)reactSetFrame:(CGRect)frame
|
834 | {
|
835 | _reactFrame = frame;
|
836 | UIViewController *parentVC = self.reactViewController.parentViewController;
|
837 | if (parentVC != nil && ![parentVC isKindOfClass:[RNSNavigationController class]]) {
|
838 | [super reactSetFrame:frame];
|
839 | }
|
840 |
|
841 |
|
842 |
|
843 |
|
844 |
|
845 |
|
846 | }
|
847 |
|
848 | - (void)invalidate
|
849 | {
|
850 | _controller = nil;
|
851 | }
|
852 | #endif
|
853 |
|
854 | @end
|
855 |
|
856 | #ifdef RCT_NEW_ARCH_ENABLED
|
857 | Class<RCTComponentViewProtocol> RNSScreenCls(void)
|
858 | {
|
859 | return RNSScreenView.class;
|
860 | }
|
861 | #endif
|
862 |
|
863 | #pragma mark - RNSScreen
|
864 |
|
865 | @implementation RNSScreen {
|
866 | __weak id _previousFirstResponder;
|
867 | CGRect _lastViewFrame;
|
868 | RNSScreenView *_initialView;
|
869 | UIView *_fakeView;
|
870 | CADisplayLink *_animationTimer;
|
871 | CGFloat _currentAlpha;
|
872 | BOOL _closing;
|
873 | BOOL _goingForward;
|
874 | int _dismissCount;
|
875 | BOOL _isSwiping;
|
876 | BOOL _shouldNotify;
|
877 | }
|
878 |
|
879 | #pragma mark - Common
|
880 |
|
881 | - (instancetype)initWithView:(UIView *)view
|
882 | {
|
883 | if (self = [super init]) {
|
884 | self.view = view;
|
885 | _fakeView = [UIView new];
|
886 | _shouldNotify = YES;
|
887 | #ifdef RCT_NEW_ARCH_ENABLED
|
888 | _initialView = (RNSScreenView *)view;
|
889 | #endif
|
890 | }
|
891 | return self;
|
892 | }
|
893 |
|
894 |
|
895 | - (void)viewWillAppear:(BOOL)animated
|
896 | {
|
897 | [super viewWillAppear:animated];
|
898 | if (!_isSwiping) {
|
899 | [self.screenView notifyWillAppear];
|
900 | if (self.transitionCoordinator.isInteractive) {
|
901 |
|
902 | _isSwiping = YES;
|
903 | }
|
904 | } else {
|
905 |
|
906 |
|
907 | _shouldNotify = NO;
|
908 | }
|
909 |
|
910 | [self hideHeaderIfNecessary];
|
911 |
|
912 | _goingForward = [self isBeingPresented] || [self isMovingToParentViewController];
|
913 |
|
914 | [RNSScreenWindowTraits updateWindowTraits];
|
915 | if (_shouldNotify) {
|
916 | _closing = NO;
|
917 | [self notifyTransitionProgress:0.0 closing:_closing goingForward:_goingForward];
|
918 | [self setupProgressNotification];
|
919 | }
|
920 | }
|
921 |
|
922 | - (void)viewWillDisappear:(BOOL)animated
|
923 | {
|
924 | [super viewWillDisappear:animated];
|
925 |
|
926 | if (!self.transitionCoordinator.isInteractive && self.navigationController != nil) {
|
927 |
|
928 |
|
929 |
|
930 |
|
931 |
|
932 | int selfIndex = [self getIndexOfView:self.screenView];
|
933 | int targetIndex = [self getIndexOfView:self.navigationController.topViewController.view];
|
934 | _dismissCount = selfIndex - targetIndex > 0 ? selfIndex - targetIndex : 1;
|
935 | } else {
|
936 | _dismissCount = 1;
|
937 | }
|
938 |
|
939 |
|
940 | if (!_isSwiping) {
|
941 | [self.screenView notifyWillDisappear];
|
942 | if (self.transitionCoordinator.isInteractive) {
|
943 | _isSwiping = YES;
|
944 | }
|
945 | } else {
|
946 | _shouldNotify = NO;
|
947 | }
|
948 |
|
949 |
|
950 | _goingForward = !([self isBeingDismissed] || [self isMovingFromParentViewController]);
|
951 |
|
952 | if (_shouldNotify) {
|
953 | _closing = YES;
|
954 | [self notifyTransitionProgress:0.0 closing:_closing goingForward:_goingForward];
|
955 | [self setupProgressNotification];
|
956 | }
|
957 | }
|
958 |
|
959 | - (void)viewDidAppear:(BOOL)animated
|
960 | {
|
961 | [super viewDidAppear:animated];
|
962 | if (!_isSwiping || _shouldNotify) {
|
963 |
|
964 |
|
965 | [self.screenView notifyAppear];
|
966 | [self notifyTransitionProgress:1.0 closing:NO goingForward:_goingForward];
|
967 | } else {
|
968 | [self.screenView notifyGestureCancel];
|
969 | }
|
970 |
|
971 | _isSwiping = NO;
|
972 | _shouldNotify = YES;
|
973 | }
|
974 |
|
975 | - (void)viewDidDisappear:(BOOL)animated
|
976 | {
|
977 | [super viewDidDisappear:animated];
|
978 | if (self.parentViewController == nil && self.presentingViewController == nil) {
|
979 | if (self.screenView.preventNativeDismiss) {
|
980 |
|
981 |
|
982 | [self.screenView.reactSuperview updateContainer];
|
983 | [self.screenView notifyDismissCancelledWithDismissCount:_dismissCount];
|
984 | } else {
|
985 |
|
986 | [self.screenView notifyDismissedWithCount:_dismissCount];
|
987 | }
|
988 | }
|
989 |
|
990 | if (!_isSwiping || _shouldNotify) {
|
991 | [self.screenView notifyDisappear];
|
992 | [self notifyTransitionProgress:1.0 closing:YES goingForward:_goingForward];
|
993 | }
|
994 |
|
995 | _isSwiping = NO;
|
996 | _shouldNotify = YES;
|
997 | #ifdef RCT_NEW_ARCH_ENABLED
|
998 | #else
|
999 | [self traverseForScrollView:self.screenView];
|
1000 | #endif
|
1001 | }
|
1002 |
|
1003 | - (void)viewDidLayoutSubviews
|
1004 | {
|
1005 | [super viewDidLayoutSubviews];
|
1006 |
|
1007 |
|
1008 |
|
1009 |
|
1010 |
|
1011 |
|
1012 | BOOL isDisplayedWithinUINavController = [self.parentViewController isKindOfClass:[RNSNavigationController class]];
|
1013 |
|
1014 |
|
1015 | if (self.screenView.isPresentedAsNativeModal) {
|
1016 | [self calculateAndNotifyHeaderHeightChangeIsModal:YES];
|
1017 | }
|
1018 |
|
1019 | if (isDisplayedWithinUINavController || self.screenView.isPresentedAsNativeModal) {
|
1020 | #ifdef RCT_NEW_ARCH_ENABLED
|
1021 | [self.screenView updateBounds];
|
1022 | #else
|
1023 | if (!CGRectEqualToRect(_lastViewFrame, self.screenView.frame)) {
|
1024 | _lastViewFrame = self.screenView.frame;
|
1025 | [((RNSScreenView *)self.viewIfLoaded) updateBounds];
|
1026 | }
|
1027 | #endif
|
1028 | }
|
1029 | }
|
1030 |
|
1031 | - (BOOL)isModalWithHeader
|
1032 | {
|
1033 | return self.screenView.isModal && self.childViewControllers.count == 1 &&
|
1034 | [self.childViewControllers[0] isKindOfClass:UINavigationController.class];
|
1035 | }
|
1036 |
|
1037 |
|
1038 |
|
1039 | - (BOOL)hasNestedStack
|
1040 | {
|
1041 | for (UIViewController *vc in self.childViewControllers) {
|
1042 | if ([vc isKindOfClass:[RNSNavigationController class]]) {
|
1043 | return YES;
|
1044 | }
|
1045 | }
|
1046 |
|
1047 | return NO;
|
1048 | }
|
1049 |
|
1050 | - (CGSize)getStatusBarHeightIsModal:(BOOL)isModal
|
1051 | {
|
1052 | #if !TARGET_OS_TV && !TARGET_OS_VISION
|
1053 | CGSize fallbackStatusBarSize = [[UIApplication sharedApplication] statusBarFrame].size;
|
1054 |
|
1055 | #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \
|
1056 | __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
|
1057 | if (@available(iOS 13.0, *)) {
|
1058 | CGSize primaryStatusBarSize = self.view.window.windowScene.statusBarManager.statusBarFrame.size;
|
1059 | if (primaryStatusBarSize.height == 0 || primaryStatusBarSize.width == 0)
|
1060 | return fallbackStatusBarSize;
|
1061 |
|
1062 | return primaryStatusBarSize;
|
1063 | } else {
|
1064 | return fallbackStatusBarSize;
|
1065 | }
|
1066 | #endif /* Check for iOS 13.0 */
|
1067 |
|
1068 | #else
|
1069 |
|
1070 | return CGSizeMake(0, 0);
|
1071 | #endif // !TARGET_OS_TV
|
1072 | }
|
1073 |
|
1074 | - (UINavigationController *)getVisibleNavigationControllerIsModal:(BOOL)isModal
|
1075 | {
|
1076 | UINavigationController *navctr = self.navigationController;
|
1077 |
|
1078 | if (isModal) {
|
1079 |
|
1080 |
|
1081 | if (self.isModalWithHeader) {
|
1082 | navctr = self.childViewControllers[0];
|
1083 | } else {
|
1084 |
|
1085 |
|
1086 |
|
1087 | return nil;
|
1088 | }
|
1089 | }
|
1090 |
|
1091 | return navctr;
|
1092 | }
|
1093 |
|
1094 | - (CGFloat)calculateHeaderHeightIsModal:(BOOL)isModal
|
1095 | {
|
1096 | UINavigationController *navctr = [self getVisibleNavigationControllerIsModal:isModal];
|
1097 |
|
1098 |
|
1099 |
|
1100 |
|
1101 | if (navctr == nil || navctr.isNavigationBarHidden) {
|
1102 | return 0;
|
1103 | }
|
1104 |
|
1105 | CGFloat navbarHeight = navctr.navigationBar.frame.size.height;
|
1106 | #if !TARGET_OS_TV
|
1107 | CGFloat navbarInset = navctr.navigationBar.frame.origin.y;
|
1108 | #else
|
1109 |
|
1110 | CGFloat navbarInset = 0;
|
1111 | #endif // !TARGET_OS_TV
|
1112 |
|
1113 | return navbarHeight + navbarInset;
|
1114 | }
|
1115 |
|
1116 | - (void)calculateAndNotifyHeaderHeightChangeIsModal:(BOOL)isModal
|
1117 | {
|
1118 | CGFloat totalHeight = [self calculateHeaderHeightIsModal:isModal];
|
1119 | [self.screenView notifyHeaderHeightChange:totalHeight];
|
1120 | }
|
1121 |
|
1122 | - (void)notifyFinishTransitioning
|
1123 | {
|
1124 | [_previousFirstResponder becomeFirstResponder];
|
1125 | _previousFirstResponder = nil;
|
1126 |
|
1127 | [RNSScreenWindowTraits updateWindowTraits];
|
1128 | }
|
1129 |
|
1130 | - (void)willMoveToParentViewController:(UIViewController *)parent
|
1131 | {
|
1132 | [super willMoveToParentViewController:parent];
|
1133 | if (parent == nil) {
|
1134 | id responder = [self findFirstResponder:self.screenView];
|
1135 | if (responder != nil) {
|
1136 | _previousFirstResponder = responder;
|
1137 | }
|
1138 | }
|
1139 | }
|
1140 |
|
1141 | - (id)findFirstResponder:(UIView *)parent
|
1142 | {
|
1143 | if (parent.isFirstResponder) {
|
1144 | return parent;
|
1145 | }
|
1146 | for (UIView *subView in parent.subviews) {
|
1147 | id responder = [self findFirstResponder:subView];
|
1148 | if (responder != nil) {
|
1149 | return responder;
|
1150 | }
|
1151 | }
|
1152 | return nil;
|
1153 | }
|
1154 |
|
1155 | #pragma mark - transition progress related methods
|
1156 |
|
1157 | - (void)setupProgressNotification
|
1158 | {
|
1159 | if (self.transitionCoordinator != nil) {
|
1160 | _fakeView.alpha = 0.0;
|
1161 | [self.transitionCoordinator
|
1162 | animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
|
1163 | [[context containerView] addSubview:self->_fakeView];
|
1164 | self->_fakeView.alpha = 1.0;
|
1165 | self->_animationTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleAnimation)];
|
1166 | [self->_animationTimer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
|
1167 | }
|
1168 | completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
|
1169 | [self->_animationTimer setPaused:YES];
|
1170 | [self->_animationTimer invalidate];
|
1171 | [self->_fakeView removeFromSuperview];
|
1172 | }];
|
1173 | }
|
1174 | }
|
1175 |
|
1176 | - (void)handleAnimation
|
1177 | {
|
1178 | if ([[_fakeView layer] presentationLayer] != nil) {
|
1179 | CGFloat fakeViewAlpha = _fakeView.layer.presentationLayer.opacity;
|
1180 | if (_currentAlpha != fakeViewAlpha) {
|
1181 | _currentAlpha = fmax(0.0, fmin(1.0, fakeViewAlpha));
|
1182 | [self notifyTransitionProgress:_currentAlpha closing:_closing goingForward:_goingForward];
|
1183 | }
|
1184 | }
|
1185 | }
|
1186 |
|
1187 | - (void)notifyTransitionProgress:(double)progress closing:(BOOL)closing goingForward:(BOOL)goingForward
|
1188 | {
|
1189 | if ([self.view isKindOfClass:[RNSScreenView class]]) {
|
1190 |
|
1191 |
|
1192 | [(RNSScreenView *)self.view notifyTransitionProgress:progress closing:closing goingForward:goingForward];
|
1193 | }
|
1194 | }
|
1195 |
|
1196 | #if !TARGET_OS_TV
|
1197 |
|
1198 |
|
1199 |
|
1200 |
|
1201 |
|
1202 | - (UIViewController *)findChildVCForConfigAndTrait:(RNSWindowTrait)trait includingModals:(BOOL)includingModals
|
1203 | {
|
1204 | UIViewController *lastViewController = [[self childViewControllers] lastObject];
|
1205 | if ([self.presentedViewController isKindOfClass:[RNSScreen class]]) {
|
1206 | lastViewController = self.presentedViewController;
|
1207 |
|
1208 | if (!includingModals) {
|
1209 | return nil;
|
1210 | }
|
1211 |
|
1212 |
|
1213 |
|
1214 |
|
1215 |
|
1216 |
|
1217 | UIViewController *modalOrChild = [(RNSScreen *)lastViewController findChildVCForConfigAndTrait:trait
|
1218 | includingModals:includingModals];
|
1219 | if (modalOrChild != nil) {
|
1220 | return modalOrChild;
|
1221 | }
|
1222 |
|
1223 |
|
1224 |
|
1225 |
|
1226 |
|
1227 |
|
1228 |
|
1229 | }
|
1230 |
|
1231 | UIViewController *selfOrNil = [self hasTraitSet:trait] ? self : nil;
|
1232 | if (lastViewController == nil) {
|
1233 | return selfOrNil;
|
1234 | } else {
|
1235 | if ([lastViewController conformsToProtocol:@protocol(RNSViewControllerDelegate)]) {
|
1236 |
|
1237 |
|
1238 |
|
1239 |
|
1240 | UIViewController *childScreen = [lastViewController childViewControllerForStatusBarStyle];
|
1241 | if ([childScreen isKindOfClass:[RNSScreen class]]) {
|
1242 | return [(RNSScreen *)childScreen findChildVCForConfigAndTrait:trait includingModals:includingModals]
|
1243 | ?: selfOrNil;
|
1244 | } else {
|
1245 | return selfOrNil;
|
1246 | }
|
1247 | } else {
|
1248 |
|
1249 | return selfOrNil;
|
1250 | }
|
1251 | }
|
1252 | }
|
1253 |
|
1254 | - (BOOL)hasTraitSet:(RNSWindowTrait)trait
|
1255 | {
|
1256 | switch (trait) {
|
1257 | case RNSWindowTraitStyle: {
|
1258 | return self.screenView.hasStatusBarStyleSet;
|
1259 | }
|
1260 | case RNSWindowTraitAnimation: {
|
1261 | return self.screenView.hasStatusBarAnimationSet;
|
1262 | }
|
1263 | case RNSWindowTraitHidden: {
|
1264 | return self.screenView.hasStatusBarHiddenSet;
|
1265 | }
|
1266 | case RNSWindowTraitOrientation: {
|
1267 | return self.screenView.hasOrientationSet;
|
1268 | }
|
1269 | case RNSWindowTraitHomeIndicatorHidden: {
|
1270 | return self.screenView.hasHomeIndicatorHiddenSet;
|
1271 | }
|
1272 | default: {
|
1273 | RCTLogError(@"Unknown trait passed: %d", (int)trait);
|
1274 | }
|
1275 | }
|
1276 | return NO;
|
1277 | }
|
1278 |
|
1279 | - (UIViewController *)childViewControllerForStatusBarHidden
|
1280 | {
|
1281 | UIViewController *vc = [self findChildVCForConfigAndTrait:RNSWindowTraitHidden includingModals:NO];
|
1282 | return vc == self ? nil : vc;
|
1283 | }
|
1284 |
|
1285 | - (BOOL)prefersStatusBarHidden
|
1286 | {
|
1287 | return self.screenView.statusBarHidden;
|
1288 | }
|
1289 |
|
1290 | - (UIViewController *)childViewControllerForStatusBarStyle
|
1291 | {
|
1292 | UIViewController *vc = [self findChildVCForConfigAndTrait:RNSWindowTraitStyle includingModals:NO];
|
1293 | return vc == self ? nil : vc;
|
1294 | }
|
1295 |
|
1296 | - (UIStatusBarStyle)preferredStatusBarStyle
|
1297 | {
|
1298 | return [RNSScreenWindowTraits statusBarStyleForRNSStatusBarStyle:self.screenView.statusBarStyle];
|
1299 | }
|
1300 |
|
1301 | - (UIStatusBarAnimation)preferredStatusBarUpdateAnimation
|
1302 | {
|
1303 | UIViewController *vc = [self findChildVCForConfigAndTrait:RNSWindowTraitAnimation includingModals:NO];
|
1304 |
|
1305 | if ([vc isKindOfClass:[RNSScreen class]]) {
|
1306 | return ((RNSScreen *)vc).screenView.statusBarAnimation;
|
1307 | }
|
1308 | return UIStatusBarAnimationFade;
|
1309 | }
|
1310 |
|
1311 | - (UIInterfaceOrientationMask)supportedInterfaceOrientations
|
1312 | {
|
1313 | UIViewController *vc = [self findChildVCForConfigAndTrait:RNSWindowTraitOrientation includingModals:YES];
|
1314 |
|
1315 | if ([vc isKindOfClass:[RNSScreen class]]) {
|
1316 | return ((RNSScreen *)vc).screenView.screenOrientation;
|
1317 | }
|
1318 | return UIInterfaceOrientationMaskAllButUpsideDown;
|
1319 | }
|
1320 |
|
1321 | - (UIViewController *)childViewControllerForHomeIndicatorAutoHidden
|
1322 | {
|
1323 | UIViewController *vc = [self findChildVCForConfigAndTrait:RNSWindowTraitHomeIndicatorHidden includingModals:YES];
|
1324 | return vc == self ? nil : vc;
|
1325 | }
|
1326 |
|
1327 | - (BOOL)prefersHomeIndicatorAutoHidden
|
1328 | {
|
1329 | return self.screenView.homeIndicatorHidden;
|
1330 | }
|
1331 | - (int)getParentChildrenCount
|
1332 | {
|
1333 | return (int)[[self.screenView.reactSuperview reactSubviews] count];
|
1334 | }
|
1335 | #endif
|
1336 |
|
1337 | - (int)getIndexOfView:(UIView *)view
|
1338 | {
|
1339 | return (int)[[self.screenView.reactSuperview reactSubviews] indexOfObject:view];
|
1340 | }
|
1341 |
|
1342 |
|
1343 |
|
1344 | - (RNSScreenView *)screenView
|
1345 | {
|
1346 | #ifdef RCT_NEW_ARCH_ENABLED
|
1347 | return _initialView;
|
1348 | #else
|
1349 | return (RNSScreenView *)self.view;
|
1350 | #endif
|
1351 | }
|
1352 |
|
1353 | - (void)hideHeaderIfNecessary
|
1354 | {
|
1355 | #if !TARGET_OS_TV
|
1356 |
|
1357 |
|
1358 |
|
1359 |
|
1360 | if (@available(iOS 13.0, *)) {
|
1361 | NSUInteger currentIndex = [self.navigationController.viewControllers indexOfObject:self];
|
1362 |
|
1363 |
|
1364 |
|
1365 | RNSScreenStackHeaderConfig *config = [self.screenView findHeaderConfig];
|
1366 | if (currentIndex > 0 && config != nil) {
|
1367 | UINavigationItem *prevNavigationItem =
|
1368 | [self.navigationController.viewControllers objectAtIndex:currentIndex - 1].navigationItem;
|
1369 | BOOL wasSearchBarActive = prevNavigationItem.searchController.active;
|
1370 |
|
1371 | #ifdef RCT_NEW_ARCH_ENABLED
|
1372 | BOOL shouldHideHeader = !config.show;
|
1373 | #else
|
1374 | BOOL shouldHideHeader = config.hide;
|
1375 | #endif
|
1376 |
|
1377 | if (wasSearchBarActive && shouldHideHeader) {
|
1378 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 0);
|
1379 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
|
1380 | [self.navigationController setNavigationBarHidden:YES animated:NO];
|
1381 | });
|
1382 | }
|
1383 | }
|
1384 | }
|
1385 | #endif
|
1386 | }
|
1387 |
|
1388 | #ifdef RCT_NEW_ARCH_ENABLED
|
1389 | #pragma mark - Fabric specific
|
1390 |
|
1391 | - (void)setViewToSnapshot:(UIView *)snapshot
|
1392 | {
|
1393 | UIView *superView = self.view.superview;
|
1394 | [self.view removeFromSuperview];
|
1395 | self.view = snapshot;
|
1396 | [superView addSubview:self.view];
|
1397 | }
|
1398 |
|
1399 | #else
|
1400 | #pragma mark - Paper specific
|
1401 |
|
1402 | - (void)traverseForScrollView:(UIView *)view
|
1403 | {
|
1404 | if (![[self.view valueForKey:@"_bridge"] valueForKey:@"_jsThread"]) {
|
1405 |
|
1406 | return;
|
1407 | }
|
1408 |
|
1409 | if ([NSStringFromClass([view class]) isEqualToString:@"AVPlayerView"]) {
|
1410 |
|
1411 |
|
1412 |
|
1413 |
|
1414 | return;
|
1415 | }
|
1416 |
|
1417 | if ([view isKindOfClass:[UIScrollView class]] &&
|
1418 | ([[(UIScrollView *)view delegate] respondsToSelector:@selector(scrollViewDidEndDecelerating:)])) {
|
1419 | [[(UIScrollView *)view delegate] scrollViewDidEndDecelerating:(id)view];
|
1420 | }
|
1421 | [view.subviews enumerateObjectsUsingBlock:^(__kindof UIView *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
|
1422 | [self traverseForScrollView:obj];
|
1423 | }];
|
1424 | }
|
1425 | #endif
|
1426 |
|
1427 | @end
|
1428 |
|
1429 | @implementation RNSScreenManager
|
1430 |
|
1431 | RCT_EXPORT_MODULE()
|
1432 |
|
1433 |
|
1434 | RCT_REMAP_VIEW_PROPERTY(activityState, activityStateOrNil, NSNumber)
|
1435 | RCT_EXPORT_VIEW_PROPERTY(customAnimationOnSwipe, BOOL);
|
1436 | RCT_EXPORT_VIEW_PROPERTY(fullScreenSwipeEnabled, BOOL);
|
1437 | RCT_EXPORT_VIEW_PROPERTY(gestureEnabled, BOOL)
|
1438 | RCT_EXPORT_VIEW_PROPERTY(gestureResponseDistance, NSDictionary)
|
1439 | RCT_EXPORT_VIEW_PROPERTY(hideKeyboardOnSwipe, BOOL)
|
1440 | RCT_EXPORT_VIEW_PROPERTY(preventNativeDismiss, BOOL)
|
1441 | RCT_EXPORT_VIEW_PROPERTY(replaceAnimation, RNSScreenReplaceAnimation)
|
1442 | RCT_EXPORT_VIEW_PROPERTY(stackPresentation, RNSScreenStackPresentation)
|
1443 | RCT_EXPORT_VIEW_PROPERTY(stackAnimation, RNSScreenStackAnimation)
|
1444 | RCT_EXPORT_VIEW_PROPERTY(swipeDirection, RNSScreenSwipeDirection)
|
1445 | RCT_EXPORT_VIEW_PROPERTY(transitionDuration, NSNumber)
|
1446 |
|
1447 | RCT_EXPORT_VIEW_PROPERTY(onAppear, RCTDirectEventBlock);
|
1448 | RCT_EXPORT_VIEW_PROPERTY(onDisappear, RCTDirectEventBlock);
|
1449 | RCT_EXPORT_VIEW_PROPERTY(onHeaderHeightChange, RCTDirectEventBlock);
|
1450 | RCT_EXPORT_VIEW_PROPERTY(onDismissed, RCTDirectEventBlock);
|
1451 | RCT_EXPORT_VIEW_PROPERTY(onNativeDismissCancelled, RCTDirectEventBlock);
|
1452 | RCT_EXPORT_VIEW_PROPERTY(onTransitionProgress, RCTDirectEventBlock);
|
1453 | RCT_EXPORT_VIEW_PROPERTY(onWillAppear, RCTDirectEventBlock);
|
1454 | RCT_EXPORT_VIEW_PROPERTY(onWillDisappear, RCTDirectEventBlock);
|
1455 | RCT_EXPORT_VIEW_PROPERTY(onGestureCancel, RCTDirectEventBlock);
|
1456 |
|
1457 | #if !TARGET_OS_TV
|
1458 | RCT_EXPORT_VIEW_PROPERTY(screenOrientation, UIInterfaceOrientationMask)
|
1459 | RCT_EXPORT_VIEW_PROPERTY(statusBarAnimation, UIStatusBarAnimation)
|
1460 | RCT_EXPORT_VIEW_PROPERTY(statusBarHidden, BOOL)
|
1461 | RCT_EXPORT_VIEW_PROPERTY(statusBarStyle, RNSStatusBarStyle)
|
1462 | RCT_EXPORT_VIEW_PROPERTY(homeIndicatorHidden, BOOL)
|
1463 |
|
1464 | RCT_EXPORT_VIEW_PROPERTY(sheetAllowedDetents, RNSScreenDetentType);
|
1465 | RCT_EXPORT_VIEW_PROPERTY(sheetLargestUndimmedDetent, RNSScreenDetentType);
|
1466 | RCT_EXPORT_VIEW_PROPERTY(sheetGrabberVisible, BOOL);
|
1467 | RCT_EXPORT_VIEW_PROPERTY(sheetCornerRadius, CGFloat);
|
1468 | RCT_EXPORT_VIEW_PROPERTY(sheetExpandsWhenScrolledToEdge, BOOL);
|
1469 | #endif
|
1470 |
|
1471 | #if !TARGET_OS_TV && !TARGET_OS_VISION
|
1472 |
|
1473 |
|
1474 |
|
1475 |
|
1476 | - (instancetype)init
|
1477 | {
|
1478 | if (self = [super init]) {
|
1479 | dispatch_async(dispatch_get_main_queue(), ^{
|
1480 | [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
|
1481 | });
|
1482 | }
|
1483 | return self;
|
1484 | }
|
1485 |
|
1486 | - (void)dealloc
|
1487 | {
|
1488 | dispatch_sync(dispatch_get_main_queue(), ^{
|
1489 | [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
|
1490 | });
|
1491 | }
|
1492 | #endif // !TARGET_OS_TV
|
1493 |
|
1494 | #ifdef RCT_NEW_ARCH_ENABLED
|
1495 | #else
|
1496 | - (UIView *)view
|
1497 | {
|
1498 | return [[RNSScreenView alloc] initWithBridge:self.bridge];
|
1499 | }
|
1500 | #endif
|
1501 |
|
1502 | + (BOOL)requiresMainQueueSetup
|
1503 | {
|
1504 |
|
1505 |
|
1506 |
|
1507 | return NO;
|
1508 | }
|
1509 |
|
1510 | @end
|
1511 |
|
1512 | @implementation RCTConvert (RNSScreen)
|
1513 |
|
1514 | RCT_ENUM_CONVERTER(
|
1515 | RNSScreenStackPresentation,
|
1516 | (@{
|
1517 | @"push" : @(RNSScreenStackPresentationPush),
|
1518 | @"modal" : @(RNSScreenStackPresentationModal),
|
1519 | @"fullScreenModal" : @(RNSScreenStackPresentationFullScreenModal),
|
1520 | @"formSheet" : @(RNSScreenStackPresentationFormSheet),
|
1521 | @"containedModal" : @(RNSScreenStackPresentationContainedModal),
|
1522 | @"transparentModal" : @(RNSScreenStackPresentationTransparentModal),
|
1523 | @"containedTransparentModal" : @(RNSScreenStackPresentationContainedTransparentModal)
|
1524 | }),
|
1525 | RNSScreenStackPresentationPush,
|
1526 | integerValue)
|
1527 |
|
1528 | RCT_ENUM_CONVERTER(
|
1529 | RNSScreenStackAnimation,
|
1530 | (@{
|
1531 | @"default" : @(RNSScreenStackAnimationDefault),
|
1532 | @"none" : @(RNSScreenStackAnimationNone),
|
1533 | @"fade" : @(RNSScreenStackAnimationFade),
|
1534 | @"fade_from_bottom" : @(RNSScreenStackAnimationFadeFromBottom),
|
1535 | @"flip" : @(RNSScreenStackAnimationFlip),
|
1536 | @"simple_push" : @(RNSScreenStackAnimationSimplePush),
|
1537 | @"slide_from_bottom" : @(RNSScreenStackAnimationSlideFromBottom),
|
1538 | @"slide_from_right" : @(RNSScreenStackAnimationDefault),
|
1539 | @"slide_from_left" : @(RNSScreenStackAnimationSlideFromLeft),
|
1540 | @"ios" : @(RNSScreenStackAnimationDefault),
|
1541 | }),
|
1542 | RNSScreenStackAnimationDefault,
|
1543 | integerValue)
|
1544 |
|
1545 | RCT_ENUM_CONVERTER(
|
1546 | RNSScreenReplaceAnimation,
|
1547 | (@{
|
1548 | @"push" : @(RNSScreenReplaceAnimationPush),
|
1549 | @"pop" : @(RNSScreenReplaceAnimationPop),
|
1550 | }),
|
1551 | RNSScreenReplaceAnimationPop,
|
1552 | integerValue)
|
1553 |
|
1554 | RCT_ENUM_CONVERTER(
|
1555 | RNSScreenSwipeDirection,
|
1556 | (@{
|
1557 | @"vertical" : @(RNSScreenSwipeDirectionVertical),
|
1558 | @"horizontal" : @(RNSScreenSwipeDirectionHorizontal),
|
1559 | }),
|
1560 | RNSScreenSwipeDirectionHorizontal,
|
1561 | integerValue)
|
1562 |
|
1563 | #if !TARGET_OS_TV
|
1564 | RCT_ENUM_CONVERTER(
|
1565 | UIStatusBarAnimation,
|
1566 | (@{
|
1567 | @"none" : @(UIStatusBarAnimationNone),
|
1568 | @"fade" : @(UIStatusBarAnimationFade),
|
1569 | @"slide" : @(UIStatusBarAnimationSlide)
|
1570 | }),
|
1571 | UIStatusBarAnimationNone,
|
1572 | integerValue)
|
1573 |
|
1574 | RCT_ENUM_CONVERTER(
|
1575 | RNSStatusBarStyle,
|
1576 | (@{
|
1577 | @"auto" : @(RNSStatusBarStyleAuto),
|
1578 | @"inverted" : @(RNSStatusBarStyleInverted),
|
1579 | @"light" : @(RNSStatusBarStyleLight),
|
1580 | @"dark" : @(RNSStatusBarStyleDark),
|
1581 | }),
|
1582 | RNSStatusBarStyleAuto,
|
1583 | integerValue)
|
1584 |
|
1585 | RCT_ENUM_CONVERTER(
|
1586 | RNSScreenDetentType,
|
1587 | (@{
|
1588 | @"large" : @(RNSScreenDetentTypeLarge),
|
1589 | @"medium" : @(RNSScreenDetentTypeMedium),
|
1590 | @"all" : @(RNSScreenDetentTypeAll),
|
1591 | }),
|
1592 | RNSScreenDetentTypeAll,
|
1593 | integerValue)
|
1594 |
|
1595 | + (UIInterfaceOrientationMask)UIInterfaceOrientationMask:(id)json
|
1596 | {
|
1597 | json = [self NSString:json];
|
1598 | if ([json isEqualToString:@"default"]) {
|
1599 | return UIInterfaceOrientationMaskAllButUpsideDown;
|
1600 | } else if ([json isEqualToString:@"all"]) {
|
1601 | return UIInterfaceOrientationMaskAll;
|
1602 | } else if ([json isEqualToString:@"portrait"]) {
|
1603 | return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown;
|
1604 | } else if ([json isEqualToString:@"portrait_up"]) {
|
1605 | return UIInterfaceOrientationMaskPortrait;
|
1606 | } else if ([json isEqualToString:@"portrait_down"]) {
|
1607 | return UIInterfaceOrientationMaskPortraitUpsideDown;
|
1608 | } else if ([json isEqualToString:@"landscape"]) {
|
1609 | return UIInterfaceOrientationMaskLandscape;
|
1610 | } else if ([json isEqualToString:@"landscape_left"]) {
|
1611 | return UIInterfaceOrientationMaskLandscapeLeft;
|
1612 | } else if ([json isEqualToString:@"landscape_right"]) {
|
1613 | return UIInterfaceOrientationMaskLandscapeRight;
|
1614 | }
|
1615 | return UIInterfaceOrientationMaskAllButUpsideDown;
|
1616 | }
|
1617 | #endif
|
1618 |
|
1619 | @end
|