react-native-screens 3.13.1 → 3.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/README.md +2 -2
  2. package/RNScreens.podspec +5 -4
  3. package/android/build.gradle +18 -1
  4. package/android/src/main/java/com/swmansion/rnscreens/Screen.kt +0 -5
  5. package/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt +8 -4
  6. package/android/src/main/java/com/swmansion/rnscreens/ScreenViewManager.kt +24 -6
  7. package/android/src/main/java/com/swmansion/rnscreens/ScreenWindowTraits.kt +17 -21
  8. package/android/src/main/jni/Android.mk +1 -2
  9. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenManagerDelegate.java +39 -14
  10. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenManagerInterface.java +15 -6
  11. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenStackHeaderConfigManagerDelegate.java +3 -3
  12. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenStackHeaderConfigManagerInterface.java +1 -1
  13. package/android/src/paper/java/com/swmansion/rnscreens/FabricEnabledViewGroup.kt +4 -10
  14. package/common/cpp/Android.mk +1 -2
  15. package/createNativeStackNavigator/README.md +4 -0
  16. package/ios/RNSConvert.h +30 -0
  17. package/ios/RNSConvert.mm +120 -0
  18. package/ios/RNSEnums.h +59 -0
  19. package/ios/RNSFullWindowOverlay.h +17 -2
  20. package/ios/RNSFullWindowOverlay.mm +199 -0
  21. package/ios/RNSScreen.h +70 -79
  22. package/ios/{RNSScreen.m → RNSScreen.mm} +678 -302
  23. package/ios/RNSScreenContainer.h +15 -1
  24. package/ios/{RNSScreenContainer.m → RNSScreenContainer.mm} +99 -8
  25. package/ios/{RNSScreenNavigationContainer.m → RNSScreenNavigationContainer.mm} +22 -0
  26. package/ios/RNSScreenStack.h +19 -3
  27. package/ios/{RNSScreenStack.m → RNSScreenStack.mm} +431 -137
  28. package/ios/{RNSScreenStackAnimator.m → RNSScreenStackAnimator.mm} +19 -14
  29. package/ios/RNSScreenStackHeaderConfig.h +20 -21
  30. package/ios/{RNSScreenStackHeaderConfig.m → RNSScreenStackHeaderConfig.mm} +232 -117
  31. package/ios/RNSScreenStackHeaderSubview.h +45 -0
  32. package/ios/RNSScreenStackHeaderSubview.mm +137 -0
  33. package/ios/RNSScreenViewEvent.h +12 -0
  34. package/ios/RNSScreenViewEvent.mm +59 -0
  35. package/ios/{RNSScreenWindowTraits.m → RNSScreenWindowTraits.mm} +3 -2
  36. package/ios/RNSSearchBar.h +14 -1
  37. package/ios/RNSSearchBar.mm +351 -0
  38. package/ios/{UIViewController+RNScreens.m → UIViewController+RNScreens.mm} +0 -0
  39. package/ios/{UIWindow+RNScreens.m → UIWindow+RNScreens.mm} +0 -0
  40. package/lib/commonjs/fabric/FullWindowOverlayNativeComponent.js +21 -0
  41. package/lib/commonjs/fabric/FullWindowOverlayNativeComponent.js.map +1 -0
  42. package/lib/commonjs/fabric/ScreenContainerNativeComponent.js +21 -0
  43. package/lib/commonjs/fabric/ScreenContainerNativeComponent.js.map +1 -0
  44. package/lib/commonjs/fabric/ScreenNativeComponent.js.map +1 -1
  45. package/lib/commonjs/fabric/ScreenNavigationContainerNativeComponent.js +21 -0
  46. package/lib/commonjs/fabric/ScreenNavigationContainerNativeComponent.js.map +1 -0
  47. package/lib/commonjs/fabric/ScreenStackHeaderConfigNativeComponent.js.map +1 -1
  48. package/lib/commonjs/fabric/ScreenStackNativeComponent.js.map +1 -1
  49. package/lib/commonjs/fabric/SearchBarNativeComponent.js +25 -0
  50. package/lib/commonjs/fabric/SearchBarNativeComponent.js.map +1 -0
  51. package/lib/commonjs/index.native.js +20 -32
  52. package/lib/commonjs/index.native.js.map +1 -1
  53. package/lib/commonjs/native-stack/views/NativeStackView.js +4 -0
  54. package/lib/commonjs/native-stack/views/NativeStackView.js.map +1 -1
  55. package/lib/commonjs/reanimated/ReanimatedNativeStackScreen.js +10 -2
  56. package/lib/commonjs/reanimated/ReanimatedNativeStackScreen.js.map +1 -1
  57. package/lib/module/fabric/FullWindowOverlayNativeComponent.js +9 -0
  58. package/lib/module/fabric/FullWindowOverlayNativeComponent.js.map +1 -0
  59. package/lib/module/fabric/ScreenContainerNativeComponent.js +9 -0
  60. package/lib/module/fabric/ScreenContainerNativeComponent.js.map +1 -0
  61. package/lib/module/fabric/ScreenNativeComponent.js.map +1 -1
  62. package/lib/module/fabric/ScreenNavigationContainerNativeComponent.js +9 -0
  63. package/lib/module/fabric/ScreenNavigationContainerNativeComponent.js.map +1 -0
  64. package/lib/module/fabric/ScreenStackHeaderConfigNativeComponent.js.map +1 -1
  65. package/lib/module/fabric/ScreenStackNativeComponent.js.map +1 -1
  66. package/lib/module/fabric/SearchBarNativeComponent.js +11 -0
  67. package/lib/module/fabric/SearchBarNativeComponent.js.map +1 -0
  68. package/lib/module/index.native.js +21 -34
  69. package/lib/module/index.native.js.map +1 -1
  70. package/lib/module/native-stack/views/NativeStackView.js +4 -0
  71. package/lib/module/native-stack/views/NativeStackView.js.map +1 -1
  72. package/lib/module/reanimated/ReanimatedNativeStackScreen.js +9 -2
  73. package/lib/module/reanimated/ReanimatedNativeStackScreen.js.map +1 -1
  74. package/lib/typescript/native-stack/types.d.ts +12 -0
  75. package/lib/typescript/reanimated/ReanimatedNativeStackScreen.d.ts +1 -1
  76. package/lib/typescript/reanimated/ReanimatedScreen.d.ts +1 -1
  77. package/lib/typescript/types.d.ts +24 -0
  78. package/native-stack/README.md +21 -0
  79. package/package.json +2 -2
  80. package/src/fabric/FullWindowOverlayNativeComponent.js +19 -0
  81. package/src/fabric/ScreenContainerNativeComponent.js +19 -0
  82. package/src/fabric/ScreenNativeComponent.js +41 -8
  83. package/src/fabric/ScreenNavigationContainerNativeComponent.js +19 -0
  84. package/src/fabric/ScreenStackHeaderConfigNativeComponent.js +1 -1
  85. package/src/fabric/ScreenStackNativeComponent.js +4 -0
  86. package/src/fabric/SearchBarNativeComponent.js +62 -0
  87. package/src/index.native.tsx +17 -36
  88. package/src/native-stack/types.tsx +12 -0
  89. package/src/native-stack/views/NativeStackView.tsx +4 -0
  90. package/src/reanimated/ReanimatedNativeStackScreen.tsx +8 -0
  91. package/src/types.tsx +25 -0
  92. package/ios/RNSFullWindowOverlay.m +0 -105
  93. package/ios/RNSScreenComponentView.h +0 -23
  94. package/ios/RNSScreenComponentView.mm +0 -159
  95. package/ios/RNSScreenController.h +0 -10
  96. package/ios/RNSScreenController.mm +0 -79
  97. package/ios/RNSScreenStackComponentView.h +0 -15
  98. package/ios/RNSScreenStackComponentView.mm +0 -295
  99. package/ios/RNSScreenStackHeaderConfigComponentView.h +0 -42
  100. package/ios/RNSScreenStackHeaderConfigComponentView.mm +0 -662
  101. package/ios/RNSScreenStackHeaderSubviewComponentView.h +0 -14
  102. package/ios/RNSScreenStackHeaderSubviewComponentView.mm +0 -77
  103. package/ios/RNSSearchBar.m +0 -198
  104. package/lib/commonjs/fabric/Screen.js +0 -29
  105. package/lib/commonjs/fabric/Screen.js.map +0 -1
  106. package/lib/commonjs/fabric/ScreenStack.js +0 -26
  107. package/lib/commonjs/fabric/ScreenStack.js.map +0 -1
  108. package/lib/commonjs/fabric/ScreenStackHeaderSubview.js +0 -37
  109. package/lib/commonjs/fabric/ScreenStackHeaderSubview.js.map +0 -1
  110. package/lib/commonjs/fabric/index.js +0 -40
  111. package/lib/commonjs/fabric/index.js.map +0 -1
  112. package/lib/module/fabric/Screen.js +0 -16
  113. package/lib/module/fabric/Screen.js.map +0 -1
  114. package/lib/module/fabric/ScreenStack.js +0 -15
  115. package/lib/module/fabric/ScreenStack.js.map +0 -1
  116. package/lib/module/fabric/ScreenStackHeaderSubview.js +0 -24
  117. package/lib/module/fabric/ScreenStackHeaderSubview.js.map +0 -1
  118. package/lib/module/fabric/index.js +0 -6
  119. package/lib/module/fabric/index.js.map +0 -1
  120. package/src/fabric/Screen.js +0 -15
  121. package/src/fabric/ScreenStack.js +0 -10
  122. package/src/fabric/ScreenStackHeaderSubview.js +0 -22
  123. package/src/fabric/index.js +0 -11
@@ -1,21 +1,39 @@
1
- #import "RNSScreenStack.h"
2
- #import "RNSScreen.h"
3
- #import "RNSScreenStackAnimator.h"
4
- #import "RNSScreenStackHeaderConfig.h"
5
- #import "RNSScreenWindowTraits.h"
1
+ #ifdef RN_FABRIC_ENABLED
2
+ #import <React/RCTMountingTransactionObserving.h>
3
+ #import <React/RCTSurfaceTouchHandler.h>
4
+ #import <React/UIView+React.h>
5
+ #import <react/renderer/components/rnscreens/ComponentDescriptors.h>
6
+ #import <react/renderer/components/rnscreens/EventEmitters.h>
7
+ #import <react/renderer/components/rnscreens/Props.h>
8
+ #import <react/renderer/components/rnscreens/RCTComponentViewHelpers.h>
6
9
 
10
+ #import "RCTFabricComponentsPlugins.h"
11
+
12
+ #else
7
13
  #import <React/RCTBridge.h>
8
14
  #import <React/RCTRootContentView.h>
9
15
  #import <React/RCTShadowView.h>
10
16
  #import <React/RCTTouchHandler.h>
11
17
  #import <React/RCTUIManager.h>
12
18
  #import <React/RCTUIManagerUtils.h>
19
+ #endif // RN_FABRIC_ENABLED
20
+
21
+ #import "RNSScreen.h"
22
+ #import "RNSScreenStack.h"
23
+ #import "RNSScreenStackAnimator.h"
24
+ #import "RNSScreenStackHeaderConfig.h"
25
+ #import "RNSScreenWindowTraits.h"
13
26
 
14
27
  @interface RNSScreenStackView () <
15
28
  UINavigationControllerDelegate,
16
29
  UIAdaptivePresentationControllerDelegate,
17
30
  UIGestureRecognizerDelegate,
18
- UIViewControllerTransitioningDelegate>
31
+ UIViewControllerTransitioningDelegate
32
+ #ifdef RN_FABRIC_ENABLED
33
+ ,
34
+ RCTMountingTransactionObserving
35
+ #endif
36
+ >
19
37
 
20
38
  @property (nonatomic) NSMutableArray<UIViewController *> *presentedModals;
21
39
  @property (nonatomic) BOOL updatingModals;
@@ -71,13 +89,29 @@
71
89
  @implementation RNSScreenStackView {
72
90
  UINavigationController *_controller;
73
91
  NSMutableArray<RNSScreenView *> *_reactSubviews;
74
- __weak RNSScreenStackManager *_manager;
75
- BOOL _hasLayout;
76
92
  BOOL _invalidated;
93
+ BOOL _isFullWidthSwiping;
77
94
  UIPercentDrivenInteractiveTransition *_interactionController;
95
+ BOOL _hasLayout;
96
+ __weak RNSScreenStackManager *_manager;
78
97
  BOOL _updateScheduled;
79
- BOOL _isFullWidthSwiping;
98
+ #ifdef RN_FABRIC_ENABLED
99
+ UIView *_snapshot;
100
+ #endif
101
+ }
102
+
103
+ #ifdef RN_FABRIC_ENABLED
104
+ - (instancetype)initWithFrame:(CGRect)frame
105
+ {
106
+ if (self = [super initWithFrame:frame]) {
107
+ static const auto defaultProps = std::make_shared<const facebook::react::RNSScreenStackProps>();
108
+ _props = defaultProps;
109
+ [self initCommonProps];
110
+ }
111
+
112
+ return self;
80
113
  }
114
+ #endif // RN_FABRIC_ENABLED
81
115
 
82
116
  - (instancetype)initWithManager:(RNSScreenStackManager *)manager
83
117
  {
@@ -85,26 +119,41 @@
85
119
  _hasLayout = NO;
86
120
  _invalidated = NO;
87
121
  _manager = manager;
88
- _reactSubviews = [NSMutableArray new];
89
- _presentedModals = [NSMutableArray new];
90
- _controller = [[RNScreensNavigationController alloc] init];
91
- _controller.delegate = self;
122
+ [self initCommonProps];
123
+ }
124
+ return self;
125
+ }
92
126
 
127
+ - (void)initCommonProps
128
+ {
129
+ _reactSubviews = [NSMutableArray new];
130
+ _presentedModals = [NSMutableArray new];
131
+ _controller = [RNScreensNavigationController new];
132
+ _controller.delegate = self;
93
133
  #if !TARGET_OS_TV
94
- [self setupGestureHandlers];
134
+ [self setupGestureHandlers];
95
135
  #endif
96
- // we have to initialize viewControllers with a non empty array for
97
- // largeTitle header to render in the opened state. If it is empty
98
- // the header will render in collapsed state which is perhaps a bug
99
- // in UIKit but ¯\_(ツ)_/¯
100
- [_controller setViewControllers:@[ [UIViewController new] ]];
101
- }
102
- return self;
136
+ // we have to initialize viewControllers with a non empty array for
137
+ // largeTitle header to render in the opened state. If it is empty
138
+ // the header will render in collapsed state which is perhaps a bug
139
+ // in UIKit but ¯\_(ツ)_/¯
140
+ [_controller setViewControllers:@[ [UIViewController new] ]];
103
141
  }
104
142
 
105
- - (UIViewController *)reactViewController
143
+ #pragma mark - Common
144
+
145
+ - (void)emitOnFinishTransitioningEvent
106
146
  {
107
- return _controller;
147
+ #ifdef RN_FABRIC_ENABLED
148
+ if (_eventEmitter != nullptr) {
149
+ std::dynamic_pointer_cast<const facebook::react::RNSScreenStackEventEmitter>(_eventEmitter)
150
+ ->onFinishTransitioning(facebook::react::RNSScreenStackEventEmitter::OnFinishTransitioning{});
151
+ }
152
+ #else
153
+ if (self.onFinishTransitioning) {
154
+ self.onFinishTransitioning(nil);
155
+ }
156
+ #endif
108
157
  }
109
158
 
110
159
  - (void)navigationController:(UINavigationController *)navigationController
@@ -112,6 +161,12 @@
112
161
  animated:(BOOL)animated
113
162
  {
114
163
  UIView *view = viewController.view;
164
+ #ifdef RN_FABRIC_ENABLED
165
+ if (![view isKindOfClass:[RNSScreenView class]]) {
166
+ // if the current view is a snapshot, config was already removed so we don't trigger the method
167
+ return;
168
+ }
169
+ #endif
115
170
  RNSScreenStackHeaderConfig *config = nil;
116
171
  for (UIView *subview in view.reactSubviews) {
117
172
  if ([subview isKindOfClass:[RNSScreenStackHeaderConfig class]]) {
@@ -122,100 +177,65 @@
122
177
  [RNSScreenStackHeaderConfig willShowViewController:viewController animated:animated withConfig:config];
123
178
  }
124
179
 
125
- - (void)navigationController:(UINavigationController *)navigationController
126
- didShowViewController:(UIViewController *)viewController
127
- animated:(BOOL)animated
128
- {
129
- if (self.onFinishTransitioning) {
130
- self.onFinishTransitioning(nil);
131
- }
132
- [RNSScreenWindowTraits updateWindowTraits];
133
- }
134
-
135
180
  - (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController
136
181
  {
137
182
  // We don't directly set presentation delegate but instead rely on the ScreenView's delegate to
138
183
  // forward certain calls to the container (Stack).
139
- UIView *screenView = presentationController.presentedViewController.view;
140
- if ([screenView isKindOfClass:[RNSScreenView class]]) {
184
+ if ([presentationController.presentedViewController isKindOfClass:[RNSScreen class]]) {
141
185
  // we trigger the update of status bar's appearance here because there is no other lifecycle method
142
186
  // that can handle it when dismissing a modal, the same for orientation
143
187
  [RNSScreenWindowTraits updateWindowTraits];
144
188
  [_presentedModals removeObject:presentationController.presentedViewController];
145
- // we double check if there are no new controllers pending to be presented since someone could
146
- // have tried to push another one during the transition
189
+
147
190
  _updatingModals = NO;
191
+ #ifdef RN_FABRIC_ENABLED
192
+ [self emitOnFinishTransitioningEvent];
193
+ #else
194
+ // we double check if there are no new controllers pending to be presented since someone could
195
+ // have tried to push another one during the transition.
196
+ // We don't do it on Fabric since update of container will be triggered from "unmount" method afterwards
148
197
  [self updateContainer];
149
198
  if (self.onFinishTransitioning) {
150
199
  // instead of directly triggering onFinishTransitioning this time we enqueue the event on the
151
200
  // main queue. We do that because onDismiss event is also enqueued and we want for the transition
152
201
  // finish event to arrive later than onDismiss (see RNSScreen#notifyDismiss)
153
202
  dispatch_async(dispatch_get_main_queue(), ^{
154
- if (self.onFinishTransitioning) {
155
- self.onFinishTransitioning(nil);
156
- }
203
+ [self emitOnFinishTransitioningEvent];
157
204
  });
158
205
  }
206
+ #endif
159
207
  }
160
208
  }
161
209
 
162
- - (void)markChildUpdated
163
- {
164
- // do nothing
165
- }
166
-
167
- - (void)didUpdateChildren
168
- {
169
- // do nothing
170
- }
171
-
172
- - (void)insertReactSubview:(RNSScreenView *)subview atIndex:(NSInteger)atIndex
173
- {
174
- if (![subview isKindOfClass:[RNSScreenView class]]) {
175
- RCTLogError(@"ScreenStack only accepts children of type Screen");
176
- return;
177
- }
178
- subview.reactSuperview = self;
179
- [_reactSubviews insertObject:subview atIndex:atIndex];
180
- }
181
-
182
- - (void)removeReactSubview:(RNSScreenView *)subview
183
- {
184
- subview.reactSuperview = nil;
185
- [_reactSubviews removeObject:subview];
186
- }
187
-
188
210
  - (NSArray<UIView *> *)reactSubviews
189
211
  {
190
212
  return _reactSubviews;
191
213
  }
192
214
 
193
- - (void)didUpdateReactSubviews
194
- {
195
- // we need to wait until children have their layout set. At this point they don't have the layout
196
- // set yet, however the layout call is already enqueued on ui thread. Enqueuing update call on the
197
- // ui queue will guarantee that the update will run after layout.
198
- dispatch_async(dispatch_get_main_queue(), ^{
199
- self->_hasLayout = YES;
200
- [self maybeAddToParentAndUpdateContainer];
201
- });
202
- }
203
-
204
215
  - (void)didMoveToWindow
205
216
  {
206
217
  [super didMoveToWindow];
218
+ #ifdef RN_FABRIC_ENABLED
219
+ // for handling nested stacks
220
+ [self maybeAddToParentAndUpdateContainer];
221
+ #else
207
222
  if (!_invalidated) {
208
223
  // We check whether the view has been invalidated before running side-effects in didMoveToWindow
209
224
  // This is needed because when LayoutAnimations are used it is possible for view to be re-attached
210
225
  // to a window despite the fact it has been removed from the React Native view hierarchy.
211
226
  [self maybeAddToParentAndUpdateContainer];
212
227
  }
228
+ #endif
213
229
  }
214
230
 
215
231
  - (void)maybeAddToParentAndUpdateContainer
216
232
  {
217
233
  BOOL wasScreenMounted = _controller.parentViewController != nil;
234
+ #ifdef RN_FABRIC_ENABLED
235
+ BOOL isScreenReadyForShowing = self.window;
236
+ #else
218
237
  BOOL isScreenReadyForShowing = self.window && _hasLayout;
238
+ #endif
219
239
  if (!isScreenReadyForShowing && !wasScreenMounted) {
220
240
  // We wait with adding to parent controller until the stack is mounted and has its initial
221
241
  // layout done.
@@ -318,9 +338,7 @@
318
338
  __weak RNSScreenStackView *weakSelf = self;
319
339
 
320
340
  void (^afterTransitions)(void) = ^{
321
- if (weakSelf.onFinishTransitioning) {
322
- weakSelf.onFinishTransitioning(nil);
323
- }
341
+ [weakSelf emitOnFinishTransitioningEvent];
324
342
  weakSelf.updatingModals = NO;
325
343
  if (weakSelf.scheduleModalsUpdate) {
326
344
  // if modals update was requested during setModalViewControllers we set scheduleModalsUpdate
@@ -364,7 +382,7 @@
364
382
  #endif
365
383
 
366
384
  BOOL shouldAnimate = lastModal && [next isKindOfClass:[RNSScreen class]] &&
367
- ((RNSScreenView *)next.view).stackAnimation != RNSScreenStackAnimationNone;
385
+ ((RNSScreen *)next).screenView.stackAnimation != RNSScreenStackAnimationNone;
368
386
 
369
387
  // if you want to present another modal quick enough after dismissing the previous one,
370
388
  // it will result in wrong changeRootController, see repro in
@@ -391,7 +409,7 @@
391
409
  [_presentedModals containsObject:changeRootController.presentedViewController]) {
392
410
  BOOL shouldAnimate = changeRootIndex == controllers.count &&
393
411
  [changeRootController.presentedViewController isKindOfClass:[RNSScreen class]] &&
394
- ((RNSScreenView *)changeRootController.presentedViewController.view).stackAnimation !=
412
+ ((RNSScreen *)changeRootController.presentedViewController).screenView.stackAnimation !=
395
413
  RNSScreenStackAnimationNone;
396
414
  [changeRootController dismissViewControllerAnimated:shouldAnimate completion:finish];
397
415
  } else {
@@ -434,33 +452,39 @@
434
452
  }
435
453
 
436
454
  UIViewController *top = controllers.lastObject;
437
- UIViewController *lastTop = _controller.viewControllers.lastObject;
455
+ #ifdef RN_FABRIC_ENABLED
456
+ UIViewController *previousTop = _controller.topViewController;
457
+ #else
458
+ UIViewController *previousTop = _controller.viewControllers.lastObject;
459
+ #endif
438
460
 
439
- // at the start we set viewControllers to contain a single UIVIewController
461
+ // At the start we set viewControllers to contain a single UIViewController
440
462
  // instance. This is a workaround for header height adjustment bug (see comment
441
463
  // in the init function). Here, we need to detect if the initial empty
442
464
  // controller is still there
443
- BOOL firstTimePush = ![lastTop isKindOfClass:[RNSScreen class]];
465
+ BOOL firstTimePush = ![previousTop isKindOfClass:[RNSScreen class]];
444
466
 
445
467
  if (firstTimePush) {
446
468
  // nothing pushed yet
447
469
  [_controller setViewControllers:controllers animated:NO];
448
- } else if (top != lastTop) {
449
- // we always provide `animated:YES` since, if the user does not want the animation, he will provide
450
- // `stackAnimation: 'none'`, which will resolve in no animation anyways.
451
- if (![controllers containsObject:lastTop]) {
470
+ } else if (top != previousTop) {
471
+ if (![controllers containsObject:previousTop]) {
452
472
  // if the previous top screen does not exist anymore and the new top was not on the stack before, probably replace
453
473
  // was called, so we check the animation
454
474
  if (![_controller.viewControllers containsObject:top] &&
455
475
  ((RNSScreenView *)top.view).replaceAnimation == RNSScreenReplaceAnimationPush) {
456
476
  // setting new controllers with animation does `push` animation by default
477
+ #ifdef RN_FABRIC_ENABLED
478
+ auto screenController = (RNSScreen *)top;
479
+ [screenController resetViewToScreen];
480
+ #endif
457
481
  [_controller setViewControllers:controllers animated:YES];
458
482
  } else {
459
483
  // last top controller is no longer on stack
460
484
  // in this case we set the controllers stack to the new list with
461
485
  // added the last top element to it and perform (animated) pop
462
486
  NSMutableArray *newControllers = [NSMutableArray arrayWithArray:controllers];
463
- [newControllers addObject:lastTop];
487
+ [newControllers addObject:previousTop];
464
488
  [_controller setViewControllers:newControllers animated:NO];
465
489
  [_controller popViewControllerAnimated:YES];
466
490
  }
@@ -471,6 +495,10 @@
471
495
  NSMutableArray *newControllers = [NSMutableArray arrayWithArray:controllers];
472
496
  [newControllers removeLastObject];
473
497
  [_controller setViewControllers:newControllers animated:NO];
498
+ #ifdef RN_FABRIC_ENABLED
499
+ auto screenController = (RNSScreen *)top;
500
+ [screenController resetViewToScreen];
501
+ #endif
474
502
  [_controller pushViewController:top animated:YES];
475
503
  } else {
476
504
  // don't really know what this case could be, but may need to handle it
@@ -506,45 +534,23 @@
506
534
  [self setModalViewControllers:modalControllers];
507
535
  }
508
536
 
509
- // By default, the header buttons that are not inside the native hit area
510
- // cannot be clicked, so we check it by ourselves
511
- - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
512
- {
513
- if (CGRectContainsPoint(_controller.navigationBar.frame, point)) {
514
- // headerConfig should be the first subview of the topmost screen
515
- UIView *headerConfig = [[_reactSubviews.lastObject reactSubviews] firstObject];
516
- if ([headerConfig isKindOfClass:[RNSScreenStackHeaderConfig class]]) {
517
- UIView *headerHitTestResult = [headerConfig hitTest:point withEvent:event];
518
- if (headerHitTestResult != nil) {
519
- return headerHitTestResult;
520
- }
521
- }
522
- }
523
- return [super hitTest:point withEvent:event];
524
- }
525
-
526
537
  - (void)layoutSubviews
527
538
  {
528
539
  [super layoutSubviews];
529
540
  _controller.view.frame = self.bounds;
530
541
  }
531
542
 
532
- - (void)invalidate
533
- {
534
- _invalidated = YES;
535
- for (UIViewController *controller in _presentedModals) {
536
- [controller dismissViewControllerAnimated:NO completion:nil];
537
- }
538
- [_presentedModals removeAllObjects];
539
- [_controller willMoveToParentViewController:nil];
540
- [_controller removeFromParentViewController];
541
- }
542
-
543
543
  - (void)dismissOnReload
544
544
  {
545
+ #ifdef RN_FABRIC_ENABLED
546
+ if ([_controller.visibleViewController isKindOfClass:[RNSScreen class]]) {
547
+ [(RNSScreen *)_controller.visibleViewController resetViewToScreen];
548
+ }
549
+ #else
545
550
  dispatch_async(dispatch_get_main_queue(), ^{
546
551
  [self invalidate];
547
552
  });
553
+ #endif
548
554
  }
549
555
 
550
556
  #pragma mark methods connected to transitioning
@@ -556,9 +562,9 @@
556
562
  {
557
563
  RNSScreenView *screen;
558
564
  if (operation == UINavigationControllerOperationPush) {
559
- screen = (RNSScreenView *)toVC.view;
565
+ screen = ((RNSScreen *)toVC).screenView;
560
566
  } else if (operation == UINavigationControllerOperationPop) {
561
- screen = (RNSScreenView *)fromVC.view;
567
+ screen = ((RNSScreen *)fromVC).screenView;
562
568
  }
563
569
  if (screen != nil &&
564
570
  // we need to return the animator when full width swiping even if the animation is not custom,
@@ -579,29 +585,29 @@
579
585
  while (parent != nil && ![parent respondsToSelector:@selector(touchHandler)])
580
586
  parent = parent.superview;
581
587
  if (parent != nil) {
588
+ #ifdef RN_FABRIC_ENABLED
589
+ RCTSurfaceTouchHandler *touchHandler = [parent performSelector:@selector(touchHandler)];
590
+ #else
582
591
  RCTTouchHandler *touchHandler = [parent performSelector:@selector(touchHandler)];
583
- [touchHandler cancel];
592
+ #endif
593
+ [touchHandler setEnabled:NO];
594
+ [touchHandler setEnabled:YES];
584
595
  [touchHandler reset];
585
596
  }
586
597
  }
587
598
 
588
599
  - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
589
600
  {
590
- RNSScreenView *topScreen = (RNSScreenView *)_controller.viewControllers.lastObject.view;
591
-
592
- if (![topScreen isKindOfClass:[RNSScreenView class]] || !topScreen.gestureEnabled ||
593
- _controller.viewControllers.count < 2) {
594
- return NO;
595
- }
601
+ RNSScreenView *topScreen = _reactSubviews.lastObject;
596
602
 
597
603
  #if TARGET_OS_TV
598
604
  [self cancelTouchesInParent];
599
605
  return YES;
600
606
  #else
601
- if (topScreen.fullScreenSwipeEnabled) {
602
- // we want only `RNSPanGestureRecognizer` to be able to recognize when
603
- // `fullScreenSwipeEnabled` is set
604
- if ([gestureRecognizer isKindOfClass:[RNSPanGestureRecognizer class]]) {
607
+ // RNSPanGestureRecognizer will receive events iff topScreen.fullScreenSwipeEnabled == YES;
608
+ // Events are filtered in gestureRecognizer:shouldReceivePressOrTouchEvent: method
609
+ if ([gestureRecognizer isKindOfClass:[RNSPanGestureRecognizer class]]) {
610
+ if ([self isInGestureResponseDistance:gestureRecognizer topScreen:topScreen]) {
605
611
  _isFullWidthSwiping = YES;
606
612
  [self cancelTouchesInParent];
607
613
  return YES;
@@ -609,6 +615,7 @@
609
615
  return NO;
610
616
  }
611
617
 
618
+ // Now we're dealing with RNSScreenEdgeGestureRecognizer (or _UIParallaxTransitionPanGestureRecognizer)
612
619
  if (topScreen.customAnimationOnSwipe && [RNSScreenStackAnimator isCustomAnimation:topScreen.stackAnimation]) {
613
620
  if ([gestureRecognizer isKindOfClass:[RNSScreenEdgeGestureRecognizer class]]) {
614
621
  // if we do not set any explicit `semanticContentAttribute`, it is `UISemanticContentAttributeUnspecified` instead
@@ -627,14 +634,13 @@
627
634
  if ([gestureRecognizer isKindOfClass:[RNSScreenEdgeGestureRecognizer class]]) {
628
635
  // it should only recognize with `customAnimationOnSwipe` set
629
636
  return NO;
630
- } else if ([gestureRecognizer isKindOfClass:[RNSPanGestureRecognizer class]]) {
631
- // it should only recognize with `fullScreenSwipeEnabled` set
632
- return NO;
633
637
  }
638
+ // _UIParallaxTransitionPanGestureRecognizer (other...)
634
639
  [self cancelTouchesInParent];
635
640
  return YES;
636
641
  }
637
- #endif
642
+
643
+ #endif // TARGET_OS_TV
638
644
  }
639
645
 
640
646
  #if !TARGET_OS_TV
@@ -662,7 +668,7 @@
662
668
 
663
669
  - (void)handleSwipe:(UIPanGestureRecognizer *)gestureRecognizer
664
670
  {
665
- RNSScreenView *topScreen = (RNSScreenView *)_controller.viewControllers.lastObject.view;
671
+ RNSScreenView *topScreen = _reactSubviews.lastObject;
666
672
  float translation;
667
673
  float velocity;
668
674
  float distance;
@@ -716,7 +722,6 @@
716
722
  }
717
723
  }
718
724
  }
719
- #endif
720
725
 
721
726
  - (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
722
727
  interactionControllerForAnimationController:
@@ -731,8 +736,294 @@
731
736
  return _interactionController;
732
737
  }
733
738
 
739
+ - (void)navigationController:(UINavigationController *)navigationController
740
+ didShowViewController:(UIViewController *)viewController
741
+ animated:(BOOL)animated
742
+ {
743
+ [self emitOnFinishTransitioningEvent];
744
+ [RNSScreenWindowTraits updateWindowTraits];
745
+ }
746
+ #endif
747
+
748
+ - (void)markChildUpdated
749
+ {
750
+ // do nothing
751
+ }
752
+
753
+ - (void)didUpdateChildren
754
+ {
755
+ // do nothing
756
+ }
757
+
758
+ - (UIViewController *)reactViewController
759
+ {
760
+ return _controller;
761
+ }
762
+
763
+ - (BOOL)isInGestureResponseDistance:(UIGestureRecognizer *)gestureRecognizer topScreen:(RNSScreenView *)topScreen
764
+ {
765
+ NSDictionary *gestureResponseDistanceValues = topScreen.gestureResponseDistance;
766
+ float x = [gestureRecognizer locationInView:gestureRecognizer.view].x;
767
+ float y = [gestureRecognizer locationInView:gestureRecognizer.view].y;
768
+ BOOL isRTL = _controller.view.semanticContentAttribute == UISemanticContentAttributeForceRightToLeft;
769
+ if (isRTL) {
770
+ x = _controller.view.frame.size.width - x;
771
+ }
772
+
773
+ // see:
774
+ // https://github.com/software-mansion/react-native-screens/pull/1442/commits/74d4bae321875d8305ad021b3d448ebf713e7d56
775
+ // this prop is always default initialized so we do not expect any nils
776
+ float start = [gestureResponseDistanceValues[@"start"] floatValue];
777
+ float end = [gestureResponseDistanceValues[@"end"] floatValue];
778
+ float top = [gestureResponseDistanceValues[@"top"] floatValue];
779
+ float bottom = [gestureResponseDistanceValues[@"bottom"] floatValue];
780
+
781
+ // we check if any of the constraints are violated and return NO if so
782
+ return !(
783
+ (start != -1 && x < start) || (end != -1 && x > end) || (top != -1 && y < top) || (bottom != -1 && y > bottom));
784
+ }
785
+
786
+ // By default, the header buttons that are not inside the native hit area
787
+ // cannot be clicked, so we check it by ourselves
788
+ - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
789
+ {
790
+ if (CGRectContainsPoint(_controller.navigationBar.frame, point)) {
791
+ // headerConfig should be the first subview of the topmost screen
792
+ UIView *headerConfig = [[_reactSubviews.lastObject reactSubviews] firstObject];
793
+ if ([headerConfig isKindOfClass:[RNSScreenStackHeaderConfig class]]) {
794
+ UIView *headerHitTestResult = [headerConfig hitTest:point withEvent:event];
795
+ if (headerHitTestResult != nil) {
796
+ return headerHitTestResult;
797
+ }
798
+ }
799
+ }
800
+ return [super hitTest:point withEvent:event];
801
+ }
802
+
803
+ #if !TARGET_OS_TV
804
+
805
+ - (BOOL)isScrollViewPanGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
806
+ {
807
+ // NOTE: This hack is required to restore native behavior of edge swipe (interactive pop gesture)
808
+ // without this, on a screen with a scroll view, it's only possible to pop view by panning horizontally
809
+ // if even slightly diagonal (or if in motion), scroll view will scroll, and edge swipe will be cancelled
810
+ if (![[gestureRecognizer view] isKindOfClass:[UIScrollView class]]) {
811
+ return NO;
812
+ }
813
+ UIScrollView *scrollView = (UIScrollView *)gestureRecognizer.view;
814
+ return scrollView.panGestureRecognizer == gestureRecognizer;
815
+ }
816
+
817
+ // Custom method for compatibility with iOS < 13.4
818
+ // RNSScreenStackView is a UIGestureRecognizerDelegate for three types of gesture recognizers:
819
+ // RNSPanGestureRecognizer, RNSScreenEdgeGestureRecognizer, _UIParallaxTransitionPanGestureRecognizer
820
+ // Be careful when adding another type of gesture recognizer.
821
+ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceivePressOrTouchEvent:(NSObject *)event
822
+ {
823
+ RNSScreenView *topScreen = _reactSubviews.lastObject;
824
+
825
+ if (![topScreen isKindOfClass:[RNSScreenView class]] || !topScreen.gestureEnabled ||
826
+ _controller.viewControllers.count < 2) {
827
+ return NO;
828
+ }
829
+
830
+ // We want to pass events to RNSPanGestureRecognizer iff full screen swipe is enabled.
831
+ if ([gestureRecognizer isKindOfClass:[RNSPanGestureRecognizer class]]) {
832
+ return topScreen.fullScreenSwipeEnabled;
833
+ }
834
+
835
+ // RNSScreenEdgeGestureRecognizer || _UIParallaxTransitionPanGestureRecognizer
836
+ return YES;
837
+ }
838
+
839
+ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceivePress:(UIPress *)press;
840
+ {
841
+ return [self gestureRecognizer:gestureRecognizer shouldReceivePressOrTouchEvent:press];
842
+ }
843
+
844
+ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch;
845
+ {
846
+ return [self gestureRecognizer:gestureRecognizer shouldReceivePressOrTouchEvent:touch];
847
+ }
848
+
849
+ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
850
+ shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
851
+ {
852
+ if ([gestureRecognizer isKindOfClass:[RNSPanGestureRecognizer class]] &&
853
+ [self isScrollViewPanGestureRecognizer:otherGestureRecognizer]) {
854
+ RNSPanGestureRecognizer *panGestureRecognizer = (RNSPanGestureRecognizer *)gestureRecognizer;
855
+ BOOL isBackGesture = [panGestureRecognizer translationInView:panGestureRecognizer.view].x > 0 &&
856
+ _controller.viewControllers.count > 1;
857
+
858
+ if (gestureRecognizer.state == UIGestureRecognizerStateBegan || isBackGesture) {
859
+ return NO;
860
+ }
861
+
862
+ return YES;
863
+ }
864
+ return NO;
865
+ }
866
+
867
+ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
868
+ shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
869
+ {
870
+ return (
871
+ [gestureRecognizer isKindOfClass:[UIScreenEdgePanGestureRecognizer class]] &&
872
+ [self isScrollViewPanGestureRecognizer:otherGestureRecognizer]);
873
+ }
874
+
875
+ #endif // !TARGET_OS_TV
876
+
877
+ - (void)insertReactSubview:(RNSScreenView *)subview atIndex:(NSInteger)atIndex
878
+ {
879
+ if (![subview isKindOfClass:[RNSScreenView class]]) {
880
+ RCTLogError(@"ScreenStack only accepts children of type Screen");
881
+ return;
882
+ }
883
+ subview.reactSuperview = self;
884
+ [_reactSubviews insertObject:subview atIndex:atIndex];
885
+ }
886
+
887
+ - (void)removeReactSubview:(RNSScreenView *)subview
888
+ {
889
+ subview.reactSuperview = nil;
890
+ [_reactSubviews removeObject:subview];
891
+ }
892
+
893
+ - (void)didUpdateReactSubviews
894
+ {
895
+ // we need to wait until children have their layout set. At this point they don't have the layout
896
+ // set yet, however the layout call is already enqueued on ui thread. Enqueuing update call on the
897
+ // ui queue will guarantee that the update will run after layout.
898
+ dispatch_async(dispatch_get_main_queue(), ^{
899
+ self->_hasLayout = YES;
900
+ [self maybeAddToParentAndUpdateContainer];
901
+ });
902
+ }
903
+
904
+ #ifdef RN_FABRIC_ENABLED
905
+ #pragma mark - Fabric specific
906
+
907
+ - (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
908
+ {
909
+ if (![childComponentView isKindOfClass:[RNSScreenView class]]) {
910
+ RCTLogError(@"ScreenStack only accepts children of type Screen");
911
+ return;
912
+ }
913
+
914
+ RCTAssert(
915
+ childComponentView.reactSuperview == nil,
916
+ @"Attempt to mount already mounted component view. (parent: %@, child: %@, index: %@, existing parent: %@)",
917
+ self,
918
+ childComponentView,
919
+ @(index),
920
+ @([childComponentView.superview tag]));
921
+
922
+ [_reactSubviews insertObject:(RNSScreenView *)childComponentView atIndex:index];
923
+ ((RNSScreenView *)childComponentView).reactSuperview = self;
924
+ dispatch_async(dispatch_get_main_queue(), ^{
925
+ [self maybeAddToParentAndUpdateContainer];
926
+ });
927
+ }
928
+
929
+ - (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
930
+ {
931
+ RNSScreenView *screenChildComponent = (RNSScreenView *)childComponentView;
932
+ // We should only do a snapshot of a screen that is on the top.
933
+ // We also check `_presentedModals` since if you push 2 modals, second one is not a "child" of _controller.
934
+ // Also, when dissmised with a gesture, the screen already is not under the window, so we don't need to apply
935
+ // snapshot.
936
+ if (screenChildComponent.window != nil &&
937
+ ((screenChildComponent == _controller.visibleViewController.view && _presentedModals.count < 2) ||
938
+ screenChildComponent == [_presentedModals.lastObject view])) {
939
+ [screenChildComponent.controller setViewToSnapshot:_snapshot];
940
+ }
941
+
942
+ RCTAssert(
943
+ screenChildComponent.reactSuperview == self,
944
+ @"Attempt to unmount a view which is mounted inside different view. (parent: %@, child: %@, index: %@)",
945
+ self,
946
+ screenChildComponent,
947
+ @(index));
948
+ RCTAssert(
949
+ (_reactSubviews.count > index) && [_reactSubviews objectAtIndex:index] == childComponentView,
950
+ @"Attempt to unmount a view which has a different index. (parent: %@, child: %@, index: %@, actual index: %@, tag at index: %@)",
951
+ self,
952
+ screenChildComponent,
953
+ @(index),
954
+ @([_reactSubviews indexOfObject:screenChildComponent]),
955
+ @([[_reactSubviews objectAtIndex:index] tag]));
956
+ screenChildComponent.reactSuperview = nil;
957
+ [_reactSubviews removeObject:screenChildComponent];
958
+ [screenChildComponent removeFromSuperview];
959
+ dispatch_async(dispatch_get_main_queue(), ^{
960
+ [self maybeAddToParentAndUpdateContainer];
961
+ });
962
+ }
963
+
964
+ - (void)takeSnapshot
965
+ {
966
+ _snapshot = [_controller.visibleViewController.view snapshotViewAfterScreenUpdates:NO];
967
+ }
968
+
969
+ - (void)mountingTransactionWillMount:(facebook::react::MountingTransaction const &)transaction
970
+ withSurfaceTelemetry:(facebook::react::SurfaceTelemetry const &)surfaceTelemetry
971
+ {
972
+ for (auto mutation : transaction.getMutations()) {
973
+ if (mutation.type == facebook::react::ShadowViewMutation::Type::Remove &&
974
+ mutation.parentShadowView.componentName != nil &&
975
+ strcmp(mutation.parentShadowView.componentName, "RNSScreenStack") == 0) {
976
+ [self takeSnapshot];
977
+ return;
978
+ }
979
+ }
980
+ }
981
+
982
+ - (void)prepareForRecycle
983
+ {
984
+ [super prepareForRecycle];
985
+ _reactSubviews = [NSMutableArray new];
986
+
987
+ for (UIViewController *controller in _presentedModals) {
988
+ [controller dismissViewControllerAnimated:NO completion:nil];
989
+ }
990
+
991
+ [_presentedModals removeAllObjects];
992
+ [self dismissOnReload];
993
+ [_controller willMoveToParentViewController:nil];
994
+ [_controller removeFromParentViewController];
995
+ [_controller setViewControllers:@[ [UIViewController new] ]];
996
+ }
997
+
998
+ + (facebook::react::ComponentDescriptorProvider)componentDescriptorProvider
999
+ {
1000
+ return facebook::react::concreteComponentDescriptorProvider<facebook::react::RNSScreenStackComponentDescriptor>();
1001
+ }
1002
+ #else
1003
+ #pragma mark - Paper specific
1004
+
1005
+ - (void)invalidate
1006
+ {
1007
+ _invalidated = YES;
1008
+ for (UIViewController *controller in _presentedModals) {
1009
+ [controller dismissViewControllerAnimated:NO completion:nil];
1010
+ }
1011
+ [_presentedModals removeAllObjects];
1012
+ [_controller willMoveToParentViewController:nil];
1013
+ [_controller removeFromParentViewController];
1014
+ }
1015
+
1016
+ #endif // RN_FABRIC_ENABLED
1017
+
734
1018
  @end
735
1019
 
1020
+ #ifdef RN_FABRIC_ENABLED
1021
+ Class<RCTComponentViewProtocol> RNSScreenStackCls(void)
1022
+ {
1023
+ return RNSScreenStackView.class;
1024
+ }
1025
+ #endif
1026
+
736
1027
  @implementation RNSScreenStackManager {
737
1028
  NSPointerArray *_stacks;
738
1029
  }
@@ -741,6 +1032,8 @@ RCT_EXPORT_MODULE()
741
1032
 
742
1033
  RCT_EXPORT_VIEW_PROPERTY(onFinishTransitioning, RCTDirectEventBlock);
743
1034
 
1035
+ #ifdef RN_FABRIC_ENABLED
1036
+ #else
744
1037
  - (UIView *)view
745
1038
  {
746
1039
  RNSScreenStackView *view = [[RNSScreenStackView alloc] initWithManager:self];
@@ -750,6 +1043,7 @@ RCT_EXPORT_VIEW_PROPERTY(onFinishTransitioning, RCTDirectEventBlock);
750
1043
  [_stacks addPointer:(__bridge void *)view];
751
1044
  return view;
752
1045
  }
1046
+ #endif // RN_FABRIC_ENABLED
753
1047
 
754
1048
  - (void)invalidate
755
1049
  {