react-native-screens 3.12.0 → 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 (141) 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 +18 -20
  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} +387 -124
  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/Screen.js +10 -8
  43. package/lib/commonjs/fabric/Screen.js.map +1 -1
  44. package/lib/commonjs/fabric/ScreenContainer.js +28 -0
  45. package/lib/commonjs/fabric/ScreenContainer.js.map +1 -0
  46. package/lib/commonjs/fabric/ScreenContainerNativeComponent.js +21 -0
  47. package/lib/commonjs/fabric/ScreenContainerNativeComponent.js.map +1 -0
  48. package/lib/commonjs/fabric/ScreenNativeComponent.js.map +1 -1
  49. package/lib/commonjs/fabric/ScreenNavigationContainer.js +28 -0
  50. package/lib/commonjs/fabric/ScreenNavigationContainer.js.map +1 -0
  51. package/lib/commonjs/fabric/ScreenNavigationContainerNativeComponent.js +21 -0
  52. package/lib/commonjs/fabric/ScreenNavigationContainerNativeComponent.js.map +1 -0
  53. package/lib/commonjs/fabric/ScreenStack.js +6 -7
  54. package/lib/commonjs/fabric/ScreenStack.js.map +1 -1
  55. package/lib/commonjs/fabric/ScreenStackHeaderConfigNativeComponent.js.map +1 -1
  56. package/lib/commonjs/fabric/ScreenStackHeaderSubview.js +4 -1
  57. package/lib/commonjs/fabric/ScreenStackHeaderSubview.js.map +1 -1
  58. package/lib/commonjs/fabric/ScreenStackNativeComponent.js.map +1 -1
  59. package/lib/commonjs/fabric/SearchBar.js +37 -0
  60. package/lib/commonjs/fabric/SearchBar.js.map +1 -0
  61. package/lib/commonjs/fabric/SearchBarNativeComponent.js +25 -0
  62. package/lib/commonjs/fabric/SearchBarNativeComponent.js.map +1 -0
  63. package/lib/commonjs/fabric/index.js +32 -0
  64. package/lib/commonjs/fabric/index.js.map +1 -1
  65. package/lib/commonjs/index.native.js +5 -18
  66. package/lib/commonjs/index.native.js.map +1 -1
  67. package/lib/commonjs/native-stack/views/NativeStackView.js +30 -0
  68. package/lib/commonjs/native-stack/views/NativeStackView.js.map +1 -1
  69. package/lib/commonjs/reanimated/ReanimatedNativeStackScreen.js +8 -2
  70. package/lib/commonjs/reanimated/ReanimatedNativeStackScreen.js.map +1 -1
  71. package/lib/module/fabric/FullWindowOverlay.js +15 -0
  72. package/lib/module/fabric/FullWindowOverlay.js.map +1 -0
  73. package/lib/module/fabric/FullWindowOverlayNativeComponent.js +9 -0
  74. package/lib/module/fabric/FullWindowOverlayNativeComponent.js.map +1 -0
  75. package/lib/module/fabric/Screen.js +8 -3
  76. package/lib/module/fabric/Screen.js.map +1 -1
  77. package/lib/module/fabric/ScreenContainer.js +17 -0
  78. package/lib/module/fabric/ScreenContainer.js.map +1 -0
  79. package/lib/module/fabric/ScreenContainerNativeComponent.js +9 -0
  80. package/lib/module/fabric/ScreenContainerNativeComponent.js.map +1 -0
  81. package/lib/module/fabric/ScreenNativeComponent.js.map +1 -1
  82. package/lib/module/fabric/ScreenNavigationContainer.js +17 -0
  83. package/lib/module/fabric/ScreenNavigationContainer.js.map +1 -0
  84. package/lib/module/fabric/ScreenNavigationContainerNativeComponent.js +9 -0
  85. package/lib/module/fabric/ScreenNavigationContainerNativeComponent.js.map +1 -0
  86. package/lib/module/fabric/ScreenStack.js +5 -2
  87. package/lib/module/fabric/ScreenStack.js.map +1 -1
  88. package/lib/module/fabric/ScreenStackHeaderConfigNativeComponent.js.map +1 -1
  89. package/lib/module/fabric/ScreenStackHeaderSubview.js +4 -1
  90. package/lib/module/fabric/ScreenStackHeaderSubview.js.map +1 -1
  91. package/lib/module/fabric/ScreenStackNativeComponent.js.map +1 -1
  92. package/lib/module/fabric/SearchBar.js +24 -0
  93. package/lib/module/fabric/SearchBar.js.map +1 -0
  94. package/lib/module/fabric/SearchBarNativeComponent.js +11 -0
  95. package/lib/module/fabric/SearchBarNativeComponent.js.map +1 -0
  96. package/lib/module/fabric/index.js +5 -1
  97. package/lib/module/fabric/index.js.map +1 -1
  98. package/lib/module/index.native.js +6 -20
  99. package/lib/module/index.native.js.map +1 -1
  100. package/lib/module/native-stack/views/NativeStackView.js +30 -0
  101. package/lib/module/native-stack/views/NativeStackView.js.map +1 -1
  102. package/lib/module/reanimated/ReanimatedNativeStackScreen.js +7 -2
  103. package/lib/module/reanimated/ReanimatedNativeStackScreen.js.map +1 -1
  104. package/lib/typescript/native-stack/types.d.ts +12 -0
  105. package/lib/typescript/reanimated/ReanimatedNativeStackScreen.d.ts +1 -1
  106. package/lib/typescript/reanimated/ReanimatedScreen.d.ts +1 -1
  107. package/lib/typescript/types.d.ts +24 -0
  108. package/native-stack/README.md +21 -0
  109. package/package.json +3 -4
  110. package/src/fabric/FullWindowOverlay.js +13 -0
  111. package/src/fabric/FullWindowOverlayNativeComponent.js +19 -0
  112. package/src/fabric/Screen.js +5 -2
  113. package/src/fabric/ScreenContainer.js +16 -0
  114. package/src/fabric/ScreenContainerNativeComponent.js +19 -0
  115. package/src/fabric/ScreenNativeComponent.js +41 -8
  116. package/src/fabric/ScreenNavigationContainer.js +16 -0
  117. package/src/fabric/ScreenNavigationContainerNativeComponent.js +19 -0
  118. package/src/fabric/ScreenStack.js +4 -2
  119. package/src/fabric/ScreenStackHeaderConfigNativeComponent.js +1 -1
  120. package/src/fabric/ScreenStackHeaderSubview.js +3 -1
  121. package/src/fabric/ScreenStackNativeComponent.js +4 -0
  122. package/src/fabric/SearchBar.js +20 -0
  123. package/src/fabric/SearchBarNativeComponent.js +62 -0
  124. package/src/fabric/index.js +8 -0
  125. package/src/index.native.tsx +13 -19
  126. package/src/native-stack/types.tsx +12 -0
  127. package/src/native-stack/views/NativeStackView.tsx +27 -0
  128. package/src/reanimated/ReanimatedNativeStackScreen.tsx +6 -0
  129. package/src/types.tsx +25 -0
  130. package/ios/RNSFullWindowOverlay.m +0 -105
  131. package/ios/RNSScreenComponentView.h +0 -23
  132. package/ios/RNSScreenComponentView.mm +0 -159
  133. package/ios/RNSScreenController.h +0 -10
  134. package/ios/RNSScreenController.mm +0 -79
  135. package/ios/RNSScreenStackComponentView.h +0 -15
  136. package/ios/RNSScreenStackComponentView.mm +0 -295
  137. package/ios/RNSScreenStackHeaderConfigComponentView.h +0 -42
  138. package/ios/RNSScreenStackHeaderConfigComponentView.mm +0 -662
  139. package/ios/RNSScreenStackHeaderSubviewComponentView.h +0 -14
  140. package/ios/RNSScreenStackHeaderSubviewComponentView.mm +0 -77
  141. 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,96 +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];
189
+
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
197
+ [self updateContainer];
145
198
  if (self.onFinishTransitioning) {
146
199
  // instead of directly triggering onFinishTransitioning this time we enqueue the event on the
147
200
  // main queue. We do that because onDismiss event is also enqueued and we want for the transition
148
201
  // finish event to arrive later than onDismiss (see RNSScreen#notifyDismiss)
149
202
  dispatch_async(dispatch_get_main_queue(), ^{
150
- if (self.onFinishTransitioning) {
151
- self.onFinishTransitioning(nil);
152
- }
203
+ [self emitOnFinishTransitioningEvent];
153
204
  });
154
205
  }
206
+ #endif
155
207
  }
156
208
  }
157
209
 
158
- - (void)markChildUpdated
159
- {
160
- // do nothing
161
- }
162
-
163
- - (void)didUpdateChildren
164
- {
165
- // do nothing
166
- }
167
-
168
- - (void)insertReactSubview:(RNSScreenView *)subview atIndex:(NSInteger)atIndex
169
- {
170
- if (![subview isKindOfClass:[RNSScreenView class]]) {
171
- RCTLogError(@"ScreenStack only accepts children of type Screen");
172
- return;
173
- }
174
- subview.reactSuperview = self;
175
- [_reactSubviews insertObject:subview atIndex:atIndex];
176
- }
177
-
178
- - (void)removeReactSubview:(RNSScreenView *)subview
179
- {
180
- subview.reactSuperview = nil;
181
- [_reactSubviews removeObject:subview];
182
- }
183
-
184
210
  - (NSArray<UIView *> *)reactSubviews
185
211
  {
186
212
  return _reactSubviews;
187
213
  }
188
214
 
189
- - (void)didUpdateReactSubviews
190
- {
191
- // we need to wait until children have their layout set. At this point they don't have the layout
192
- // set yet, however the layout call is already enqueued on ui thread. Enqueuing update call on the
193
- // ui queue will guarantee that the update will run after layout.
194
- dispatch_async(dispatch_get_main_queue(), ^{
195
- self->_hasLayout = YES;
196
- [self maybeAddToParentAndUpdateContainer];
197
- });
198
- }
199
-
200
215
  - (void)didMoveToWindow
201
216
  {
202
217
  [super didMoveToWindow];
218
+ #ifdef RN_FABRIC_ENABLED
219
+ // for handling nested stacks
220
+ [self maybeAddToParentAndUpdateContainer];
221
+ #else
203
222
  if (!_invalidated) {
204
223
  // We check whether the view has been invalidated before running side-effects in didMoveToWindow
205
224
  // This is needed because when LayoutAnimations are used it is possible for view to be re-attached
206
225
  // to a window despite the fact it has been removed from the React Native view hierarchy.
207
226
  [self maybeAddToParentAndUpdateContainer];
208
227
  }
228
+ #endif
209
229
  }
210
230
 
211
231
  - (void)maybeAddToParentAndUpdateContainer
212
232
  {
213
233
  BOOL wasScreenMounted = _controller.parentViewController != nil;
234
+ #ifdef RN_FABRIC_ENABLED
235
+ BOOL isScreenReadyForShowing = self.window;
236
+ #else
214
237
  BOOL isScreenReadyForShowing = self.window && _hasLayout;
238
+ #endif
215
239
  if (!isScreenReadyForShowing && !wasScreenMounted) {
216
240
  // We wait with adding to parent controller until the stack is mounted and has its initial
217
241
  // layout done.
@@ -314,9 +338,7 @@
314
338
  __weak RNSScreenStackView *weakSelf = self;
315
339
 
316
340
  void (^afterTransitions)(void) = ^{
317
- if (weakSelf.onFinishTransitioning) {
318
- weakSelf.onFinishTransitioning(nil);
319
- }
341
+ [weakSelf emitOnFinishTransitioningEvent];
320
342
  weakSelf.updatingModals = NO;
321
343
  if (weakSelf.scheduleModalsUpdate) {
322
344
  // if modals update was requested during setModalViewControllers we set scheduleModalsUpdate
@@ -360,7 +382,15 @@
360
382
  #endif
361
383
 
362
384
  BOOL shouldAnimate = lastModal && [next isKindOfClass:[RNSScreen class]] &&
363
- ((RNSScreenView *)next.view).stackAnimation != RNSScreenStackAnimationNone;
385
+ ((RNSScreen *)next).screenView.stackAnimation != RNSScreenStackAnimationNone;
386
+
387
+ // if you want to present another modal quick enough after dismissing the previous one,
388
+ // it will result in wrong changeRootController, see repro in
389
+ // https://github.com/software-mansion/react-native-screens/issues/1299 We call `updateContainer` again in
390
+ // `presentationControllerDidDismiss` to cover this case and present new controller
391
+ if (previous.beingDismissed) {
392
+ return;
393
+ }
364
394
 
365
395
  [previous presentViewController:next
366
396
  animated:shouldAnimate
@@ -379,7 +409,7 @@
379
409
  [_presentedModals containsObject:changeRootController.presentedViewController]) {
380
410
  BOOL shouldAnimate = changeRootIndex == controllers.count &&
381
411
  [changeRootController.presentedViewController isKindOfClass:[RNSScreen class]] &&
382
- ((RNSScreenView *)changeRootController.presentedViewController.view).stackAnimation !=
412
+ ((RNSScreen *)changeRootController.presentedViewController).screenView.stackAnimation !=
383
413
  RNSScreenStackAnimationNone;
384
414
  [changeRootController dismissViewControllerAnimated:shouldAnimate completion:finish];
385
415
  } else {
@@ -422,33 +452,39 @@
422
452
  }
423
453
 
424
454
  UIViewController *top = controllers.lastObject;
425
- 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
426
460
 
427
- // at the start we set viewControllers to contain a single UIVIewController
461
+ // At the start we set viewControllers to contain a single UIViewController
428
462
  // instance. This is a workaround for header height adjustment bug (see comment
429
463
  // in the init function). Here, we need to detect if the initial empty
430
464
  // controller is still there
431
- BOOL firstTimePush = ![lastTop isKindOfClass:[RNSScreen class]];
465
+ BOOL firstTimePush = ![previousTop isKindOfClass:[RNSScreen class]];
432
466
 
433
467
  if (firstTimePush) {
434
468
  // nothing pushed yet
435
469
  [_controller setViewControllers:controllers animated:NO];
436
- } else if (top != lastTop) {
437
- // we always provide `animated:YES` since, if the user does not want the animation, he will provide
438
- // `stackAnimation: 'none'`, which will resolve in no animation anyways.
439
- if (![controllers containsObject:lastTop]) {
470
+ } else if (top != previousTop) {
471
+ if (![controllers containsObject:previousTop]) {
440
472
  // if the previous top screen does not exist anymore and the new top was not on the stack before, probably replace
441
473
  // was called, so we check the animation
442
474
  if (![_controller.viewControllers containsObject:top] &&
443
475
  ((RNSScreenView *)top.view).replaceAnimation == RNSScreenReplaceAnimationPush) {
444
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
445
481
  [_controller setViewControllers:controllers animated:YES];
446
482
  } else {
447
483
  // last top controller is no longer on stack
448
484
  // in this case we set the controllers stack to the new list with
449
485
  // added the last top element to it and perform (animated) pop
450
486
  NSMutableArray *newControllers = [NSMutableArray arrayWithArray:controllers];
451
- [newControllers addObject:lastTop];
487
+ [newControllers addObject:previousTop];
452
488
  [_controller setViewControllers:newControllers animated:NO];
453
489
  [_controller popViewControllerAnimated:YES];
454
490
  }
@@ -459,6 +495,10 @@
459
495
  NSMutableArray *newControllers = [NSMutableArray arrayWithArray:controllers];
460
496
  [newControllers removeLastObject];
461
497
  [_controller setViewControllers:newControllers animated:NO];
498
+ #ifdef RN_FABRIC_ENABLED
499
+ auto screenController = (RNSScreen *)top;
500
+ [screenController resetViewToScreen];
501
+ #endif
462
502
  [_controller pushViewController:top animated:YES];
463
503
  } else {
464
504
  // don't really know what this case could be, but may need to handle it
@@ -494,45 +534,23 @@
494
534
  [self setModalViewControllers:modalControllers];
495
535
  }
496
536
 
497
- // By default, the header buttons that are not inside the native hit area
498
- // cannot be clicked, so we check it by ourselves
499
- - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
500
- {
501
- if (CGRectContainsPoint(_controller.navigationBar.frame, point)) {
502
- // headerConfig should be the first subview of the topmost screen
503
- UIView *headerConfig = [[_reactSubviews.lastObject reactSubviews] firstObject];
504
- if ([headerConfig isKindOfClass:[RNSScreenStackHeaderConfig class]]) {
505
- UIView *headerHitTestResult = [headerConfig hitTest:point withEvent:event];
506
- if (headerHitTestResult != nil) {
507
- return headerHitTestResult;
508
- }
509
- }
510
- }
511
- return [super hitTest:point withEvent:event];
512
- }
513
-
514
537
  - (void)layoutSubviews
515
538
  {
516
539
  [super layoutSubviews];
517
540
  _controller.view.frame = self.bounds;
518
541
  }
519
542
 
520
- - (void)invalidate
521
- {
522
- _invalidated = YES;
523
- for (UIViewController *controller in _presentedModals) {
524
- [controller dismissViewControllerAnimated:NO completion:nil];
525
- }
526
- [_presentedModals removeAllObjects];
527
- [_controller willMoveToParentViewController:nil];
528
- [_controller removeFromParentViewController];
529
- }
530
-
531
543
  - (void)dismissOnReload
532
544
  {
545
+ #ifdef RN_FABRIC_ENABLED
546
+ if ([_controller.visibleViewController isKindOfClass:[RNSScreen class]]) {
547
+ [(RNSScreen *)_controller.visibleViewController resetViewToScreen];
548
+ }
549
+ #else
533
550
  dispatch_async(dispatch_get_main_queue(), ^{
534
551
  [self invalidate];
535
552
  });
553
+ #endif
536
554
  }
537
555
 
538
556
  #pragma mark methods connected to transitioning
@@ -544,9 +562,9 @@
544
562
  {
545
563
  RNSScreenView *screen;
546
564
  if (operation == UINavigationControllerOperationPush) {
547
- screen = (RNSScreenView *)toVC.view;
565
+ screen = ((RNSScreen *)toVC).screenView;
548
566
  } else if (operation == UINavigationControllerOperationPop) {
549
- screen = (RNSScreenView *)fromVC.view;
567
+ screen = ((RNSScreen *)fromVC).screenView;
550
568
  }
551
569
  if (screen != nil &&
552
570
  // we need to return the animator when full width swiping even if the animation is not custom,
@@ -567,8 +585,13 @@
567
585
  while (parent != nil && ![parent respondsToSelector:@selector(touchHandler)])
568
586
  parent = parent.superview;
569
587
  if (parent != nil) {
588
+ #ifdef RN_FABRIC_ENABLED
589
+ RCTSurfaceTouchHandler *touchHandler = [parent performSelector:@selector(touchHandler)];
590
+ #else
570
591
  RCTTouchHandler *touchHandler = [parent performSelector:@selector(touchHandler)];
571
- [touchHandler cancel];
592
+ #endif
593
+ [touchHandler setEnabled:NO];
594
+ [touchHandler setEnabled:YES];
572
595
  [touchHandler reset];
573
596
  }
574
597
  }
@@ -588,8 +611,9 @@
588
611
  #else
589
612
  if (topScreen.fullScreenSwipeEnabled) {
590
613
  // we want only `RNSPanGestureRecognizer` to be able to recognize when
591
- // `fullScreenSwipeEnabled` is set
592
- 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]) {
593
617
  _isFullWidthSwiping = YES;
594
618
  [self cancelTouchesInParent];
595
619
  return YES;
@@ -622,7 +646,8 @@
622
646
  [self cancelTouchesInParent];
623
647
  return YES;
624
648
  }
625
- #endif
649
+
650
+ #endif // TARGET_OS_TV
626
651
  }
627
652
 
628
653
  #if !TARGET_OS_TV
@@ -650,7 +675,7 @@
650
675
 
651
676
  - (void)handleSwipe:(UIPanGestureRecognizer *)gestureRecognizer
652
677
  {
653
- RNSScreenView *topScreen = (RNSScreenView *)_controller.viewControllers.lastObject.view;
678
+ RNSScreenView *topScreen = ((RNSScreen *)_controller.viewControllers.lastObject).screenView;
654
679
  float translation;
655
680
  float velocity;
656
681
  float distance;
@@ -704,7 +729,6 @@
704
729
  }
705
730
  }
706
731
  }
707
- #endif
708
732
 
709
733
  - (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
710
734
  interactionControllerForAnimationController:
@@ -719,8 +743,244 @@
719
743
  return _interactionController;
720
744
  }
721
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
+
722
975
  @end
723
976
 
977
+ #ifdef RN_FABRIC_ENABLED
978
+ Class<RCTComponentViewProtocol> RNSScreenStackCls(void)
979
+ {
980
+ return RNSScreenStackView.class;
981
+ }
982
+ #endif
983
+
724
984
  @implementation RNSScreenStackManager {
725
985
  NSPointerArray *_stacks;
726
986
  }
@@ -729,6 +989,8 @@ RCT_EXPORT_MODULE()
729
989
 
730
990
  RCT_EXPORT_VIEW_PROPERTY(onFinishTransitioning, RCTDirectEventBlock);
731
991
 
992
+ #ifdef RN_FABRIC_ENABLED
993
+ #else
732
994
  - (UIView *)view
733
995
  {
734
996
  RNSScreenStackView *view = [[RNSScreenStackView alloc] initWithManager:self];
@@ -738,6 +1000,7 @@ RCT_EXPORT_VIEW_PROPERTY(onFinishTransitioning, RCTDirectEventBlock);
738
1000
  [_stacks addPointer:(__bridge void *)view];
739
1001
  return view;
740
1002
  }
1003
+ #endif // RN_FABRIC_ENABLED
741
1004
 
742
1005
  - (void)invalidate
743
1006
  {