react-native-screens 3.13.1 → 3.14.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 (126) 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/ScreenStack.kt +8 -4
  5. package/android/src/main/java/com/swmansion/rnscreens/ScreenViewManager.kt +24 -6
  6. package/android/src/main/java/com/swmansion/rnscreens/ScreenWindowTraits.kt +14 -18
  7. package/android/src/main/jni/Android.mk +1 -2
  8. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenManagerDelegate.java +39 -14
  9. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenManagerInterface.java +15 -6
  10. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenStackHeaderConfigManagerDelegate.java +3 -3
  11. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenStackHeaderConfigManagerInterface.java +1 -1
  12. package/common/cpp/Android.mk +1 -2
  13. package/createNativeStackNavigator/README.md +4 -0
  14. package/ios/RNSConvert.h +30 -0
  15. package/ios/RNSConvert.mm +120 -0
  16. package/ios/RNSEnums.h +59 -0
  17. package/ios/RNSFullWindowOverlay.h +17 -2
  18. package/ios/RNSFullWindowOverlay.mm +199 -0
  19. package/ios/RNSScreen.h +70 -79
  20. package/ios/{RNSScreen.m → RNSScreen.mm} +679 -302
  21. package/ios/RNSScreenContainer.h +15 -1
  22. package/ios/{RNSScreenContainer.m → RNSScreenContainer.mm} +99 -8
  23. package/ios/{RNSScreenNavigationContainer.m → RNSScreenNavigationContainer.mm} +22 -0
  24. package/ios/RNSScreenStack.h +19 -3
  25. package/ios/{RNSScreenStack.m → RNSScreenStack.mm} +377 -126
  26. package/ios/{RNSScreenStackAnimator.m → RNSScreenStackAnimator.mm} +19 -14
  27. package/ios/RNSScreenStackHeaderConfig.h +20 -21
  28. package/ios/{RNSScreenStackHeaderConfig.m → RNSScreenStackHeaderConfig.mm} +232 -117
  29. package/ios/RNSScreenStackHeaderSubview.h +45 -0
  30. package/ios/RNSScreenStackHeaderSubview.mm +137 -0
  31. package/ios/RNSScreenViewEvent.h +12 -0
  32. package/ios/RNSScreenViewEvent.mm +59 -0
  33. package/ios/{RNSScreenWindowTraits.m → RNSScreenWindowTraits.mm} +3 -2
  34. package/ios/RNSSearchBar.h +14 -1
  35. package/ios/RNSSearchBar.mm +351 -0
  36. package/ios/{UIViewController+RNScreens.m → UIViewController+RNScreens.mm} +0 -0
  37. package/ios/{UIWindow+RNScreens.m → UIWindow+RNScreens.mm} +0 -0
  38. package/lib/commonjs/fabric/FullWindowOverlay.js +26 -0
  39. package/lib/commonjs/fabric/FullWindowOverlay.js.map +1 -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/ScreenContainer.js +28 -0
  43. package/lib/commonjs/fabric/ScreenContainer.js.map +1 -0
  44. package/lib/commonjs/fabric/ScreenContainerNativeComponent.js +21 -0
  45. package/lib/commonjs/fabric/ScreenContainerNativeComponent.js.map +1 -0
  46. package/lib/commonjs/fabric/ScreenNativeComponent.js.map +1 -1
  47. package/lib/commonjs/fabric/ScreenNavigationContainer.js +28 -0
  48. package/lib/commonjs/fabric/ScreenNavigationContainer.js.map +1 -0
  49. package/lib/commonjs/fabric/ScreenNavigationContainerNativeComponent.js +21 -0
  50. package/lib/commonjs/fabric/ScreenNavigationContainerNativeComponent.js.map +1 -0
  51. package/lib/commonjs/fabric/ScreenStackHeaderConfigNativeComponent.js.map +1 -1
  52. package/lib/commonjs/fabric/ScreenStackNativeComponent.js.map +1 -1
  53. package/lib/commonjs/fabric/SearchBar.js +37 -0
  54. package/lib/commonjs/fabric/SearchBar.js.map +1 -0
  55. package/lib/commonjs/fabric/SearchBarNativeComponent.js +25 -0
  56. package/lib/commonjs/fabric/SearchBarNativeComponent.js.map +1 -0
  57. package/lib/commonjs/fabric/index.js +32 -0
  58. package/lib/commonjs/fabric/index.js.map +1 -1
  59. package/lib/commonjs/index.native.js +5 -18
  60. package/lib/commonjs/index.native.js.map +1 -1
  61. package/lib/commonjs/native-stack/views/NativeStackView.js +30 -0
  62. package/lib/commonjs/native-stack/views/NativeStackView.js.map +1 -1
  63. package/lib/commonjs/reanimated/ReanimatedNativeStackScreen.js +8 -2
  64. package/lib/commonjs/reanimated/ReanimatedNativeStackScreen.js.map +1 -1
  65. package/lib/module/fabric/FullWindowOverlay.js +15 -0
  66. package/lib/module/fabric/FullWindowOverlay.js.map +1 -0
  67. package/lib/module/fabric/FullWindowOverlayNativeComponent.js +9 -0
  68. package/lib/module/fabric/FullWindowOverlayNativeComponent.js.map +1 -0
  69. package/lib/module/fabric/ScreenContainer.js +17 -0
  70. package/lib/module/fabric/ScreenContainer.js.map +1 -0
  71. package/lib/module/fabric/ScreenContainerNativeComponent.js +9 -0
  72. package/lib/module/fabric/ScreenContainerNativeComponent.js.map +1 -0
  73. package/lib/module/fabric/ScreenNativeComponent.js.map +1 -1
  74. package/lib/module/fabric/ScreenNavigationContainer.js +17 -0
  75. package/lib/module/fabric/ScreenNavigationContainer.js.map +1 -0
  76. package/lib/module/fabric/ScreenNavigationContainerNativeComponent.js +9 -0
  77. package/lib/module/fabric/ScreenNavigationContainerNativeComponent.js.map +1 -0
  78. package/lib/module/fabric/ScreenStackHeaderConfigNativeComponent.js.map +1 -1
  79. package/lib/module/fabric/ScreenStackNativeComponent.js.map +1 -1
  80. package/lib/module/fabric/SearchBar.js +24 -0
  81. package/lib/module/fabric/SearchBar.js.map +1 -0
  82. package/lib/module/fabric/SearchBarNativeComponent.js +11 -0
  83. package/lib/module/fabric/SearchBarNativeComponent.js.map +1 -0
  84. package/lib/module/fabric/index.js +5 -1
  85. package/lib/module/fabric/index.js.map +1 -1
  86. package/lib/module/index.native.js +6 -20
  87. package/lib/module/index.native.js.map +1 -1
  88. package/lib/module/native-stack/views/NativeStackView.js +30 -0
  89. package/lib/module/native-stack/views/NativeStackView.js.map +1 -1
  90. package/lib/module/reanimated/ReanimatedNativeStackScreen.js +7 -2
  91. package/lib/module/reanimated/ReanimatedNativeStackScreen.js.map +1 -1
  92. package/lib/typescript/native-stack/types.d.ts +12 -0
  93. package/lib/typescript/reanimated/ReanimatedNativeStackScreen.d.ts +1 -1
  94. package/lib/typescript/reanimated/ReanimatedScreen.d.ts +1 -1
  95. package/lib/typescript/types.d.ts +24 -0
  96. package/native-stack/README.md +21 -0
  97. package/package.json +2 -2
  98. package/src/fabric/FullWindowOverlay.js +13 -0
  99. package/src/fabric/FullWindowOverlayNativeComponent.js +19 -0
  100. package/src/fabric/ScreenContainer.js +16 -0
  101. package/src/fabric/ScreenContainerNativeComponent.js +19 -0
  102. package/src/fabric/ScreenNativeComponent.js +41 -8
  103. package/src/fabric/ScreenNavigationContainer.js +16 -0
  104. package/src/fabric/ScreenNavigationContainerNativeComponent.js +19 -0
  105. package/src/fabric/ScreenStackHeaderConfigNativeComponent.js +1 -1
  106. package/src/fabric/ScreenStackNativeComponent.js +4 -0
  107. package/src/fabric/SearchBar.js +20 -0
  108. package/src/fabric/SearchBarNativeComponent.js +62 -0
  109. package/src/fabric/index.js +8 -0
  110. package/src/index.native.tsx +13 -19
  111. package/src/native-stack/types.tsx +12 -0
  112. package/src/native-stack/views/NativeStackView.tsx +27 -0
  113. package/src/reanimated/ReanimatedNativeStackScreen.tsx +6 -0
  114. package/src/types.tsx +25 -0
  115. package/ios/RNSFullWindowOverlay.m +0 -105
  116. package/ios/RNSScreenComponentView.h +0 -23
  117. package/ios/RNSScreenComponentView.mm +0 -159
  118. package/ios/RNSScreenController.h +0 -10
  119. package/ios/RNSScreenController.mm +0 -79
  120. package/ios/RNSScreenStackComponentView.h +0 -15
  121. package/ios/RNSScreenStackComponentView.mm +0 -295
  122. package/ios/RNSScreenStackHeaderConfigComponentView.h +0 -42
  123. package/ios/RNSScreenStackHeaderConfigComponentView.mm +0 -662
  124. package/ios/RNSScreenStackHeaderSubviewComponentView.h +0 -14
  125. package/ios/RNSScreenStackHeaderSubviewComponentView.mm +0 -77
  126. package/ios/RNSSearchBar.m +0 -198
@@ -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,8 +585,13 @@
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
  }
@@ -600,8 +611,9 @@
600
611
  #else
601
612
  if (topScreen.fullScreenSwipeEnabled) {
602
613
  // we want only `RNSPanGestureRecognizer` to be able to recognize when
603
- // `fullScreenSwipeEnabled` is set
604
- if ([gestureRecognizer isKindOfClass:[RNSPanGestureRecognizer class]]) {
614
+ // `fullScreenSwipeEnabled` is set, and we are in the bounds set by user
615
+ if ([gestureRecognizer isKindOfClass:[RNSPanGestureRecognizer class]] &&
616
+ [self isInGestureResponseDistance:gestureRecognizer topScreen:topScreen]) {
605
617
  _isFullWidthSwiping = YES;
606
618
  [self cancelTouchesInParent];
607
619
  return YES;
@@ -634,7 +646,8 @@
634
646
  [self cancelTouchesInParent];
635
647
  return YES;
636
648
  }
637
- #endif
649
+
650
+ #endif // TARGET_OS_TV
638
651
  }
639
652
 
640
653
  #if !TARGET_OS_TV
@@ -662,7 +675,7 @@
662
675
 
663
676
  - (void)handleSwipe:(UIPanGestureRecognizer *)gestureRecognizer
664
677
  {
665
- RNSScreenView *topScreen = (RNSScreenView *)_controller.viewControllers.lastObject.view;
678
+ RNSScreenView *topScreen = ((RNSScreen *)_controller.viewControllers.lastObject).screenView;
666
679
  float translation;
667
680
  float velocity;
668
681
  float distance;
@@ -716,7 +729,6 @@
716
729
  }
717
730
  }
718
731
  }
719
- #endif
720
732
 
721
733
  - (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
722
734
  interactionControllerForAnimationController:
@@ -731,8 +743,244 @@
731
743
  return _interactionController;
732
744
  }
733
745
 
746
+ - (void)navigationController:(UINavigationController *)navigationController
747
+ didShowViewController:(UIViewController *)viewController
748
+ animated:(BOOL)animated
749
+ {
750
+ [self emitOnFinishTransitioningEvent];
751
+ [RNSScreenWindowTraits updateWindowTraits];
752
+ }
753
+ #endif
754
+
755
+ - (void)markChildUpdated
756
+ {
757
+ // do nothing
758
+ }
759
+
760
+ - (void)didUpdateChildren
761
+ {
762
+ // do nothing
763
+ }
764
+
765
+ - (UIViewController *)reactViewController
766
+ {
767
+ return _controller;
768
+ }
769
+
770
+ - (BOOL)isInGestureResponseDistance:(UIGestureRecognizer *)gestureRecognizer topScreen:(RNSScreenView *)topScreen
771
+ {
772
+ NSDictionary *gestureResponseDistanceValues = topScreen.gestureResponseDistance;
773
+ float x = [gestureRecognizer locationInView:gestureRecognizer.view].x;
774
+ float y = [gestureRecognizer locationInView:gestureRecognizer.view].y;
775
+ BOOL isRTL = _controller.view.semanticContentAttribute == UISemanticContentAttributeForceRightToLeft;
776
+ if (isRTL) {
777
+ x = _controller.view.frame.size.width - x;
778
+ }
779
+
780
+ // see:
781
+ // https://github.com/software-mansion/react-native-screens/pull/1442/commits/74d4bae321875d8305ad021b3d448ebf713e7d56
782
+ // this prop is always default initialized so we do not expect any nils
783
+ float start = [gestureResponseDistanceValues[@"start"] floatValue];
784
+ float end = [gestureResponseDistanceValues[@"end"] floatValue];
785
+ float top = [gestureResponseDistanceValues[@"top"] floatValue];
786
+ float bottom = [gestureResponseDistanceValues[@"bottom"] floatValue];
787
+
788
+ // we check if any of the constraints are violated and return NO if so
789
+ return !(
790
+ (start != -1 && x < start) || (end != -1 && x > end) || (top != -1 && y < top) || (bottom != -1 && y > bottom));
791
+ }
792
+
793
+ // By default, the header buttons that are not inside the native hit area
794
+ // cannot be clicked, so we check it by ourselves
795
+ - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
796
+ {
797
+ if (CGRectContainsPoint(_controller.navigationBar.frame, point)) {
798
+ // headerConfig should be the first subview of the topmost screen
799
+ UIView *headerConfig = [[_reactSubviews.lastObject reactSubviews] firstObject];
800
+ if ([headerConfig isKindOfClass:[RNSScreenStackHeaderConfig class]]) {
801
+ UIView *headerHitTestResult = [headerConfig hitTest:point withEvent:event];
802
+ if (headerHitTestResult != nil) {
803
+ return headerHitTestResult;
804
+ }
805
+ }
806
+ }
807
+ return [super hitTest:point withEvent:event];
808
+ }
809
+
810
+ - (BOOL)isScrollViewPanGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
811
+ {
812
+ // NOTE: This hack is required to restore native behavior of edge swipe (interactive pop gesture)
813
+ // without this, on a screen with a scroll view, it's only possible to pop view by panning horizontally
814
+ // if even slightly diagonal (or if in motion), scroll view will scroll, and edge swipe will be cancelled
815
+ if (![[gestureRecognizer view] isKindOfClass:[UIScrollView class]]) {
816
+ return NO;
817
+ }
818
+ UIScrollView *scrollView = (UIScrollView *)gestureRecognizer.view;
819
+ return scrollView.panGestureRecognizer == gestureRecognizer;
820
+ }
821
+
822
+ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
823
+ shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
824
+ {
825
+ return [self isScrollViewPanGestureRecognizer:otherGestureRecognizer];
826
+ }
827
+
828
+ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
829
+ shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
830
+ {
831
+ return [self isScrollViewPanGestureRecognizer:otherGestureRecognizer];
832
+ }
833
+
834
+ - (void)insertReactSubview:(RNSScreenView *)subview atIndex:(NSInteger)atIndex
835
+ {
836
+ if (![subview isKindOfClass:[RNSScreenView class]]) {
837
+ RCTLogError(@"ScreenStack only accepts children of type Screen");
838
+ return;
839
+ }
840
+ subview.reactSuperview = self;
841
+ [_reactSubviews insertObject:subview atIndex:atIndex];
842
+ }
843
+
844
+ - (void)removeReactSubview:(RNSScreenView *)subview
845
+ {
846
+ subview.reactSuperview = nil;
847
+ [_reactSubviews removeObject:subview];
848
+ }
849
+
850
+ - (void)didUpdateReactSubviews
851
+ {
852
+ // we need to wait until children have their layout set. At this point they don't have the layout
853
+ // set yet, however the layout call is already enqueued on ui thread. Enqueuing update call on the
854
+ // ui queue will guarantee that the update will run after layout.
855
+ dispatch_async(dispatch_get_main_queue(), ^{
856
+ self->_hasLayout = YES;
857
+ [self maybeAddToParentAndUpdateContainer];
858
+ });
859
+ }
860
+
861
+ #ifdef RN_FABRIC_ENABLED
862
+ #pragma mark - Fabric specific
863
+
864
+ - (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
865
+ {
866
+ if (![childComponentView isKindOfClass:[RNSScreenView class]]) {
867
+ RCTLogError(@"ScreenStack only accepts children of type Screen");
868
+ return;
869
+ }
870
+
871
+ RCTAssert(
872
+ childComponentView.reactSuperview == nil,
873
+ @"Attempt to mount already mounted component view. (parent: %@, child: %@, index: %@, existing parent: %@)",
874
+ self,
875
+ childComponentView,
876
+ @(index),
877
+ @([childComponentView.superview tag]));
878
+
879
+ [_reactSubviews insertObject:(RNSScreenView *)childComponentView atIndex:index];
880
+ ((RNSScreenView *)childComponentView).reactSuperview = self;
881
+ dispatch_async(dispatch_get_main_queue(), ^{
882
+ [self maybeAddToParentAndUpdateContainer];
883
+ });
884
+ }
885
+
886
+ - (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
887
+ {
888
+ RNSScreenView *screenChildComponent = (RNSScreenView *)childComponentView;
889
+ // We should only do a snapshot of a screen that is on the top.
890
+ // We also check `_presentedModals` since if you push 2 modals, second one is not a "child" of _controller.
891
+ // Also, when dissmised with a gesture, the screen already is not under the window, so we don't need to apply
892
+ // snapshot.
893
+ if (screenChildComponent.window != nil &&
894
+ ((screenChildComponent == _controller.visibleViewController.view && _presentedModals.count < 2) ||
895
+ screenChildComponent == [_presentedModals.lastObject view])) {
896
+ [screenChildComponent.controller setViewToSnapshot:_snapshot];
897
+ }
898
+
899
+ RCTAssert(
900
+ screenChildComponent.reactSuperview == self,
901
+ @"Attempt to unmount a view which is mounted inside different view. (parent: %@, child: %@, index: %@)",
902
+ self,
903
+ screenChildComponent,
904
+ @(index));
905
+ RCTAssert(
906
+ (_reactSubviews.count > index) && [_reactSubviews objectAtIndex:index] == childComponentView,
907
+ @"Attempt to unmount a view which has a different index. (parent: %@, child: %@, index: %@, actual index: %@, tag at index: %@)",
908
+ self,
909
+ screenChildComponent,
910
+ @(index),
911
+ @([_reactSubviews indexOfObject:screenChildComponent]),
912
+ @([[_reactSubviews objectAtIndex:index] tag]));
913
+ screenChildComponent.reactSuperview = nil;
914
+ [_reactSubviews removeObject:screenChildComponent];
915
+ [screenChildComponent removeFromSuperview];
916
+ dispatch_async(dispatch_get_main_queue(), ^{
917
+ [self maybeAddToParentAndUpdateContainer];
918
+ });
919
+ }
920
+
921
+ - (void)takeSnapshot
922
+ {
923
+ _snapshot = [_controller.visibleViewController.view snapshotViewAfterScreenUpdates:NO];
924
+ }
925
+
926
+ - (void)mountingTransactionWillMount:(facebook::react::MountingTransaction const &)transaction
927
+ withSurfaceTelemetry:(facebook::react::SurfaceTelemetry const &)surfaceTelemetry
928
+ {
929
+ for (auto mutation : transaction.getMutations()) {
930
+ if (mutation.type == facebook::react::ShadowViewMutation::Type::Remove &&
931
+ mutation.parentShadowView.componentName != nil &&
932
+ strcmp(mutation.parentShadowView.componentName, "RNSScreenStack") == 0) {
933
+ [self takeSnapshot];
934
+ return;
935
+ }
936
+ }
937
+ }
938
+
939
+ - (void)prepareForRecycle
940
+ {
941
+ [super prepareForRecycle];
942
+ _reactSubviews = [NSMutableArray new];
943
+
944
+ for (UIViewController *controller in _presentedModals) {
945
+ [controller dismissViewControllerAnimated:NO completion:nil];
946
+ }
947
+
948
+ [_presentedModals removeAllObjects];
949
+ [self dismissOnReload];
950
+ [_controller willMoveToParentViewController:nil];
951
+ [_controller removeFromParentViewController];
952
+ [_controller setViewControllers:@[ [UIViewController new] ]];
953
+ }
954
+
955
+ + (facebook::react::ComponentDescriptorProvider)componentDescriptorProvider
956
+ {
957
+ return facebook::react::concreteComponentDescriptorProvider<facebook::react::RNSScreenStackComponentDescriptor>();
958
+ }
959
+ #else
960
+ #pragma mark - Paper specific
961
+
962
+ - (void)invalidate
963
+ {
964
+ _invalidated = YES;
965
+ for (UIViewController *controller in _presentedModals) {
966
+ [controller dismissViewControllerAnimated:NO completion:nil];
967
+ }
968
+ [_presentedModals removeAllObjects];
969
+ [_controller willMoveToParentViewController:nil];
970
+ [_controller removeFromParentViewController];
971
+ }
972
+
973
+ #endif // RN_FABRIC_ENABLED
974
+
734
975
  @end
735
976
 
977
+ #ifdef RN_FABRIC_ENABLED
978
+ Class<RCTComponentViewProtocol> RNSScreenStackCls(void)
979
+ {
980
+ return RNSScreenStackView.class;
981
+ }
982
+ #endif
983
+
736
984
  @implementation RNSScreenStackManager {
737
985
  NSPointerArray *_stacks;
738
986
  }
@@ -741,6 +989,8 @@ RCT_EXPORT_MODULE()
741
989
 
742
990
  RCT_EXPORT_VIEW_PROPERTY(onFinishTransitioning, RCTDirectEventBlock);
743
991
 
992
+ #ifdef RN_FABRIC_ENABLED
993
+ #else
744
994
  - (UIView *)view
745
995
  {
746
996
  RNSScreenStackView *view = [[RNSScreenStackView alloc] initWithManager:self];
@@ -750,6 +1000,7 @@ RCT_EXPORT_VIEW_PROPERTY(onFinishTransitioning, RCTDirectEventBlock);
750
1000
  [_stacks addPointer:(__bridge void *)view];
751
1001
  return view;
752
1002
  }
1003
+ #endif // RN_FABRIC_ENABLED
753
1004
 
754
1005
  - (void)invalidate
755
1006
  {