react-native-screens 3.31.1 → 3.33.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 (203) hide show
  1. package/README.md +21 -11
  2. package/RNScreens.podspec +11 -52
  3. package/android/CMakeLists.txt +48 -4
  4. package/android/build.gradle +16 -9
  5. package/android/src/fabric/java/com/swmansion/rnscreens/FabricEnabledViewGroup.kt +25 -16
  6. package/android/src/fabric/java/com/swmansion/rnscreens/NativeProxy.kt +53 -0
  7. package/android/src/main/cpp/NativeProxy.cpp +51 -0
  8. package/android/src/main/cpp/NativeProxy.h +35 -0
  9. package/android/src/main/cpp/OnLoad.cpp +8 -0
  10. package/android/src/main/cpp/jni-adapter.cpp +86 -93
  11. package/android/src/main/java/com/swmansion/rnscreens/CustomSearchView.kt +7 -2
  12. package/android/src/main/java/com/swmansion/rnscreens/CustomToolbar.kt +6 -1
  13. package/android/src/main/java/com/swmansion/rnscreens/FragmentBackPressOverrider.kt +2 -2
  14. package/android/src/main/java/com/swmansion/rnscreens/RNScreensPackage.kt +36 -17
  15. package/android/src/main/java/com/swmansion/rnscreens/Screen.kt +150 -40
  16. package/android/src/main/java/com/swmansion/rnscreens/ScreenContainer.kt +52 -30
  17. package/android/src/main/java/com/swmansion/rnscreens/ScreenContainerViewManager.kt +27 -4
  18. package/android/src/main/java/com/swmansion/rnscreens/ScreenEventDispatcher.kt +10 -2
  19. package/android/src/main/java/com/swmansion/rnscreens/ScreenFragment.kt +56 -27
  20. package/android/src/main/java/com/swmansion/rnscreens/ScreenFragmentWrapper.kt +8 -1
  21. package/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt +50 -19
  22. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt +63 -39
  23. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragmentWrapper.kt +4 -0
  24. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.kt +88 -57
  25. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfigViewManager.kt +131 -36
  26. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderSubview.kt +19 -4
  27. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderSubviewManager.kt +16 -10
  28. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackViewManager.kt +28 -25
  29. package/android/src/main/java/com/swmansion/rnscreens/ScreenViewManager.kt +177 -77
  30. package/android/src/main/java/com/swmansion/rnscreens/ScreenWindowTraits.kt +77 -25
  31. package/android/src/main/java/com/swmansion/rnscreens/ScreensModule.kt +31 -9
  32. package/android/src/main/java/com/swmansion/rnscreens/ScreensShadowNode.kt +3 -1
  33. package/android/src/main/java/com/swmansion/rnscreens/SearchBarManager.kt +160 -54
  34. package/android/src/main/java/com/swmansion/rnscreens/SearchBarView.kt +29 -22
  35. package/android/src/main/java/com/swmansion/rnscreens/SearchViewFormatter.kt +7 -2
  36. package/android/src/main/java/com/swmansion/rnscreens/events/HeaderAttachedEvent.kt +4 -1
  37. package/android/src/main/java/com/swmansion/rnscreens/events/HeaderBackButtonClickedEvent.kt +4 -1
  38. package/android/src/main/java/com/swmansion/rnscreens/events/HeaderDetachedEvent.kt +4 -1
  39. package/android/src/main/java/com/swmansion/rnscreens/events/HeaderHeightChangeEvent.kt +5 -6
  40. package/android/src/main/java/com/swmansion/rnscreens/events/ScreenAppearEvent.kt +4 -1
  41. package/android/src/main/java/com/swmansion/rnscreens/events/ScreenDisappearEvent.kt +4 -1
  42. package/android/src/main/java/com/swmansion/rnscreens/events/ScreenDismissedEvent.kt +8 -4
  43. package/android/src/main/java/com/swmansion/rnscreens/events/ScreenTransitionProgressEvent.kt +7 -6
  44. package/android/src/main/java/com/swmansion/rnscreens/events/ScreenWillAppearEvent.kt +4 -1
  45. package/android/src/main/java/com/swmansion/rnscreens/events/ScreenWillDisappearEvent.kt +4 -1
  46. package/android/src/main/java/com/swmansion/rnscreens/events/SearchBarBlurEvent.kt +5 -2
  47. package/android/src/main/java/com/swmansion/rnscreens/events/SearchBarChangeTextEvent.kt +4 -3
  48. package/android/src/main/java/com/swmansion/rnscreens/events/SearchBarCloseEvent.kt +4 -1
  49. package/android/src/main/java/com/swmansion/rnscreens/events/SearchBarFocusEvent.kt +5 -2
  50. package/android/src/main/java/com/swmansion/rnscreens/events/SearchBarOpenEvent.kt +4 -1
  51. package/android/src/main/java/com/swmansion/rnscreens/events/SearchBarSearchButtonPressEvent.kt +9 -4
  52. package/android/src/main/java/com/swmansion/rnscreens/events/StackFinishTransitioningEvent.kt +4 -1
  53. package/android/src/main/java/com/swmansion/rnscreens/utils/DeviceUtils.kt +1 -5
  54. package/android/src/main/java/com/swmansion/rnscreens/utils/ScreenDummyLayoutHelper.kt +214 -0
  55. package/android/src/main/jni/CMakeLists.txt +5 -4
  56. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenContainerManagerDelegate.java +25 -0
  57. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenContainerManagerInterface.java +16 -0
  58. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenManagerDelegate.java +6 -0
  59. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenManagerInterface.java +2 -0
  60. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenStackHeaderConfigManagerDelegate.java +3 -0
  61. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenStackHeaderConfigManagerInterface.java +1 -0
  62. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSSearchBarManagerDelegate.java +99 -0
  63. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSSearchBarManagerInterface.java +37 -0
  64. package/android/src/paper/java/com/swmansion/rnscreens/FabricEnabledViewGroup.kt +10 -5
  65. package/android/src/paper/java/com/swmansion/rnscreens/NativeProxy.kt +19 -0
  66. package/android/src/paper/java/com/swmansion/rnscreens/NativeScreensModuleSpec.java +4 -0
  67. package/common/cpp/react/renderer/components/rnscreens/FrameCorrectionModes.h +51 -0
  68. package/common/cpp/react/renderer/components/rnscreens/RNSModalScreenComponentDescriptor.h +8 -9
  69. package/common/cpp/react/renderer/components/rnscreens/RNSModalScreenShadowNode.cpp +2 -1
  70. package/common/cpp/react/renderer/components/rnscreens/RNSModalScreenShadowNode.h +9 -8
  71. package/common/cpp/react/renderer/components/rnscreens/RNSScreenComponentDescriptor.h +147 -10
  72. package/common/cpp/react/renderer/components/rnscreens/RNSScreenShadowNode.cpp +51 -1
  73. package/common/cpp/react/renderer/components/rnscreens/RNSScreenShadowNode.h +29 -7
  74. package/common/cpp/react/renderer/components/rnscreens/RNSScreenState.cpp +22 -1
  75. package/common/cpp/react/renderer/components/rnscreens/RNSScreenState.h +30 -10
  76. package/common/cpp/react/renderer/components/rnscreens/utils/RectUtil.h +36 -0
  77. package/cpp/RNSScreenRemovalListener.cpp +25 -0
  78. package/cpp/RNSScreenRemovalListener.h +20 -0
  79. package/cpp/RNScreensTurboModule.cpp +31 -23
  80. package/cpp/RNScreensTurboModule.h +17 -20
  81. package/ios/RNSConvert.h +7 -0
  82. package/ios/RNSConvert.mm +24 -0
  83. package/ios/RNSModalScreen.mm +22 -0
  84. package/ios/RNSModule.mm +2 -3
  85. package/ios/RNSScreen.h +2 -1
  86. package/ios/RNSScreen.mm +35 -19
  87. package/ios/RNSScreenContainer.mm +1 -1
  88. package/ios/RNSScreenStack.mm +59 -54
  89. package/ios/RNSScreenStackAnimator.mm +43 -6
  90. package/ios/RNSScreenStackHeaderConfig.h +2 -0
  91. package/ios/RNSScreenStackHeaderConfig.mm +93 -28
  92. package/ios/RNSScreenStackHeaderSubview.mm +8 -0
  93. package/ios/RNSSearchBar.h +5 -5
  94. package/ios/RNSSearchBar.mm +11 -11
  95. package/ios/utils/RCTSurfaceTouchHandler+RNSUtility.h +15 -0
  96. package/ios/utils/RCTSurfaceTouchHandler+RNSUtility.mm +14 -0
  97. package/ios/utils/RCTTouchHandler+RNSUtility.h +15 -0
  98. package/ios/utils/RCTTouchHandler+RNSUtility.mm +15 -0
  99. package/ios/utils/UIView+RNSUtility.h +23 -0
  100. package/ios/utils/UIView+RNSUtility.mm +55 -0
  101. package/lib/commonjs/components/Screen.js +119 -127
  102. package/lib/commonjs/components/Screen.js.map +1 -1
  103. package/lib/commonjs/components/ScreenStack.js +8 -1
  104. package/lib/commonjs/components/ScreenStack.js.map +1 -1
  105. package/lib/commonjs/components/SearchBar.js +39 -36
  106. package/lib/commonjs/components/SearchBar.js.map +1 -1
  107. package/lib/commonjs/fabric/ModalScreenNativeComponent.js.map +1 -1
  108. package/lib/commonjs/fabric/ScreenNativeComponent.js.map +1 -1
  109. package/lib/commonjs/fabric/ScreenStackHeaderConfigNativeComponent.js.map +1 -1
  110. package/lib/commonjs/native-stack/views/HeaderConfig.js +2 -0
  111. package/lib/commonjs/native-stack/views/HeaderConfig.js.map +1 -1
  112. package/lib/commonjs/native-stack/views/NativeStackView.js +4 -0
  113. package/lib/commonjs/native-stack/views/NativeStackView.js.map +1 -1
  114. package/lib/module/components/Screen.js +118 -126
  115. package/lib/module/components/Screen.js.map +1 -1
  116. package/lib/module/components/ScreenStack.js +8 -1
  117. package/lib/module/components/ScreenStack.js.map +1 -1
  118. package/lib/module/components/SearchBar.js +39 -36
  119. package/lib/module/components/SearchBar.js.map +1 -1
  120. package/lib/module/fabric/ModalScreenNativeComponent.js.map +1 -1
  121. package/lib/module/fabric/ScreenNativeComponent.js.map +1 -1
  122. package/lib/module/fabric/ScreenStackHeaderConfigNativeComponent.js.map +1 -1
  123. package/lib/module/native-stack/views/HeaderConfig.js +2 -0
  124. package/lib/module/native-stack/views/HeaderConfig.js.map +1 -1
  125. package/lib/module/native-stack/views/NativeStackView.js +4 -0
  126. package/lib/module/native-stack/views/NativeStackView.js.map +1 -1
  127. package/lib/typescript/TransitionProgressContext.d.ts +1 -1
  128. package/lib/typescript/TransitionProgressContext.d.ts.map +1 -1
  129. package/lib/typescript/components/Screen.d.ts +3 -14
  130. package/lib/typescript/components/Screen.d.ts.map +1 -1
  131. package/lib/typescript/components/ScreenStack.d.ts.map +1 -1
  132. package/lib/typescript/components/SearchBar.d.ts +14 -21
  133. package/lib/typescript/components/SearchBar.d.ts.map +1 -1
  134. package/lib/typescript/fabric/ModalScreenNativeComponent.d.ts +12 -10
  135. package/lib/typescript/fabric/ModalScreenNativeComponent.d.ts.map +1 -1
  136. package/lib/typescript/fabric/ScreenNativeComponent.d.ts +12 -10
  137. package/lib/typescript/fabric/ScreenNativeComponent.d.ts.map +1 -1
  138. package/lib/typescript/fabric/ScreenStackHeaderConfigNativeComponent.d.ts +5 -3
  139. package/lib/typescript/fabric/ScreenStackHeaderConfigNativeComponent.d.ts.map +1 -1
  140. package/lib/typescript/fabric/ScreenStackHeaderSubviewNativeComponent.d.ts +1 -1
  141. package/lib/typescript/fabric/ScreenStackHeaderSubviewNativeComponent.d.ts.map +1 -1
  142. package/lib/typescript/fabric/ScreenStackNativeComponent.d.ts +1 -1
  143. package/lib/typescript/fabric/ScreenStackNativeComponent.d.ts.map +1 -1
  144. package/lib/typescript/fabric/SearchBarNativeComponent.d.ts +9 -9
  145. package/lib/typescript/fabric/SearchBarNativeComponent.d.ts.map +1 -1
  146. package/lib/typescript/gesture-handler/RNScreensTurboModule.d.ts +1 -1
  147. package/lib/typescript/gesture-handler/RNScreensTurboModule.d.ts.map +1 -1
  148. package/lib/typescript/native-stack/types.d.ts +39 -14
  149. package/lib/typescript/native-stack/types.d.ts.map +1 -1
  150. package/lib/typescript/native-stack/utils/SafeAreaProviderCompat.d.ts +1 -1
  151. package/lib/typescript/native-stack/utils/SafeAreaProviderCompat.d.ts.map +1 -1
  152. package/lib/typescript/native-stack/utils/getDefaultHeaderHeight.d.ts +1 -1
  153. package/lib/typescript/native-stack/utils/getDefaultHeaderHeight.d.ts.map +1 -1
  154. package/lib/typescript/native-stack/utils/useAnimatedHeaderHeight.d.ts +1 -1
  155. package/lib/typescript/native-stack/utils/useAnimatedHeaderHeight.d.ts.map +1 -1
  156. package/lib/typescript/native-stack/views/HeaderConfig.d.ts +2 -2
  157. package/lib/typescript/native-stack/views/HeaderConfig.d.ts.map +1 -1
  158. package/lib/typescript/native-stack/views/NativeStackView.d.ts +1 -1
  159. package/lib/typescript/native-stack/views/NativeStackView.d.ts.map +1 -1
  160. package/lib/typescript/reanimated/ReanimatedTransitionProgressContext.d.ts +1 -1
  161. package/lib/typescript/reanimated/ReanimatedTransitionProgressContext.d.ts.map +1 -1
  162. package/lib/typescript/types.d.ts +39 -13
  163. package/lib/typescript/types.d.ts.map +1 -1
  164. package/lib/typescript/useTransitionProgress.d.ts +3 -3
  165. package/native-stack/README.md +116 -98
  166. package/package.json +16 -7
  167. package/react-native.config.js +17 -15
  168. package/src/TransitionProgressContext.tsx +1 -1
  169. package/src/components/Screen.tsx +31 -37
  170. package/src/components/ScreenStack.tsx +11 -1
  171. package/src/components/ScreenStackHeaderConfig.tsx +5 -5
  172. package/src/components/ScreenStackHeaderConfig.web.tsx +6 -6
  173. package/src/components/SearchBar.tsx +77 -65
  174. package/src/core.ts +1 -1
  175. package/src/fabric/ModalScreenNativeComponent.ts +2 -0
  176. package/src/fabric/ScreenNativeComponent.ts +2 -0
  177. package/src/fabric/ScreenNavigationContainerNativeComponent.ts +1 -1
  178. package/src/fabric/ScreenStackHeaderConfigNativeComponent.ts +4 -1
  179. package/src/fabric/ScreenStackHeaderSubviewNativeComponent.ts +1 -1
  180. package/src/fabric/SearchBarNativeComponent.ts +7 -7
  181. package/src/gesture-handler/ScreenGestureDetector.tsx +5 -5
  182. package/src/gesture-handler/constraints.ts +5 -5
  183. package/src/gesture-handler/fabricUtils.ts +1 -1
  184. package/src/native-stack/contexts/GHContext.tsx +1 -1
  185. package/src/native-stack/navigators/createNativeStackNavigator.tsx +3 -3
  186. package/src/native-stack/types.tsx +29 -4
  187. package/src/native-stack/utils/getDefaultHeaderHeight.tsx +1 -1
  188. package/src/native-stack/utils/getStatusBarHeight.tsx +1 -1
  189. package/src/native-stack/utils/useAnimatedHeaderHeight.tsx +1 -1
  190. package/src/native-stack/utils/useBackPressSubscription.tsx +1 -1
  191. package/src/native-stack/utils/useHeaderHeight.tsx +1 -1
  192. package/src/native-stack/views/FontProcessor.tsx +1 -1
  193. package/src/native-stack/views/HeaderConfig.tsx +3 -1
  194. package/src/native-stack/views/NativeStackView.tsx +13 -9
  195. package/src/reanimated/ReanimatedHeaderHeightContext.tsx +1 -1
  196. package/src/reanimated/ReanimatedNativeStackScreen.tsx +5 -5
  197. package/src/reanimated/ReanimatedScreen.tsx +2 -2
  198. package/src/reanimated/ReanimatedScreenProvider.tsx +1 -1
  199. package/src/reanimated/useReanimatedHeaderHeight.tsx +1 -1
  200. package/src/reanimated/useReanimatedTransitionProgress.tsx +1 -1
  201. package/src/types.tsx +31 -5
  202. package/src/useTransitionProgress.tsx +1 -1
  203. package/windows/README.md +4 -1
@@ -9,6 +9,9 @@ static const float RNSFadeOpenTransitionDurationProportion = 0.2 / 0.35;
9
9
  static const float RNSSlideCloseTransitionDurationProportion = 0.25 / 0.35;
10
10
  static const float RNSFadeCloseTransitionDurationProportion = 0.15 / 0.35;
11
11
  static const float RNSFadeCloseDelayTransitionDurationProportion = 0.1 / 0.35;
12
+ // same value is used in other projects using similar approach for transistions
13
+ // and it looks the most similar to the value used by Apple
14
+ static constexpr float RNSShadowViewMaxAlpha = 0.1;
12
15
 
13
16
  @implementation RNSScreenStackAnimator {
14
17
  UINavigationControllerOperation _operation;
@@ -71,17 +74,22 @@ static const float RNSFadeCloseDelayTransitionDurationProportion = 0.1 / 0.35;
71
74
  // we are swiping with full width gesture
72
75
  if (screen.customAnimationOnSwipe) {
73
76
  [self animateTransitionWithStackAnimation:screen.stackAnimation
77
+ shadowEnabled:screen.fullScreenSwipeShadowEnabled
74
78
  transitionContext:transitionContext
75
79
  toVC:toViewController
76
80
  fromVC:fromViewController];
77
81
  } else {
78
82
  // we have to provide an animation when swiping, otherwise the screen will be popped immediately,
79
83
  // so in case of no custom animation on swipe set, we provide the one closest to the default
80
- [self animateSimplePushWithTransitionContext:transitionContext toVC:toViewController fromVC:fromViewController];
84
+ [self animateSimplePushWithShadowEnabled:screen.fullScreenSwipeShadowEnabled
85
+ transitionContext:transitionContext
86
+ toVC:toViewController
87
+ fromVC:fromViewController];
81
88
  }
82
89
  } else {
83
90
  // we are going forward or provided custom animation on swipe or clicked native header back button
84
91
  [self animateTransitionWithStackAnimation:screen.stackAnimation
92
+ shadowEnabled:screen.fullScreenSwipeShadowEnabled
85
93
  transitionContext:transitionContext
86
94
  toVC:toViewController
87
95
  fromVC:fromViewController];
@@ -89,9 +97,10 @@ static const float RNSFadeCloseDelayTransitionDurationProportion = 0.1 / 0.35;
89
97
  }
90
98
  }
91
99
 
92
- - (void)animateSimplePushWithTransitionContext:(id<UIViewControllerContextTransitioning>)transitionContext
93
- toVC:(UIViewController *)toViewController
94
- fromVC:(UIViewController *)fromViewController
100
+ - (void)animateSimplePushWithShadowEnabled:(BOOL)shadowEnabled
101
+ transitionContext:(id<UIViewControllerContextTransitioning>)transitionContext
102
+ toVC:(UIViewController *)toViewController
103
+ fromVC:(UIViewController *)fromViewController
95
104
  {
96
105
  float containerWidth = transitionContext.containerView.bounds.size.width;
97
106
  float belowViewWidth = containerWidth * 0.3;
@@ -105,15 +114,32 @@ static const float RNSFadeCloseDelayTransitionDurationProportion = 0.1 / 0.35;
105
114
  leftTransform = CGAffineTransformMakeTranslation(belowViewWidth, 0);
106
115
  }
107
116
 
117
+ UIView *shadowView;
118
+ if (shadowEnabled) {
119
+ shadowView = [[UIView alloc] initWithFrame:fromViewController.view.frame];
120
+ shadowView.backgroundColor = [UIColor blackColor];
121
+ }
122
+
108
123
  if (_operation == UINavigationControllerOperationPush) {
109
124
  toViewController.view.transform = rightTransform;
110
125
  [[transitionContext containerView] addSubview:toViewController.view];
126
+ if (shadowView) {
127
+ [[transitionContext containerView] insertSubview:shadowView belowSubview:toViewController.view];
128
+ shadowView.alpha = 0.0;
129
+ }
130
+
111
131
  [UIView animateWithDuration:[self transitionDuration:transitionContext]
112
132
  animations:^{
113
133
  fromViewController.view.transform = leftTransform;
114
134
  toViewController.view.transform = CGAffineTransformIdentity;
135
+ if (shadowView) {
136
+ shadowView.alpha = RNSShadowViewMaxAlpha;
137
+ }
115
138
  }
116
139
  completion:^(BOOL finished) {
140
+ if (shadowView) {
141
+ [shadowView removeFromSuperview];
142
+ }
117
143
  fromViewController.view.transform = CGAffineTransformIdentity;
118
144
  toViewController.view.transform = CGAffineTransformIdentity;
119
145
  [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
@@ -121,12 +147,22 @@ static const float RNSFadeCloseDelayTransitionDurationProportion = 0.1 / 0.35;
121
147
  } else if (_operation == UINavigationControllerOperationPop) {
122
148
  toViewController.view.transform = leftTransform;
123
149
  [[transitionContext containerView] insertSubview:toViewController.view belowSubview:fromViewController.view];
150
+ if (shadowView) {
151
+ [[transitionContext containerView] insertSubview:shadowView belowSubview:fromViewController.view];
152
+ shadowView.alpha = RNSShadowViewMaxAlpha;
153
+ }
124
154
 
125
155
  void (^animationBlock)(void) = ^{
126
156
  toViewController.view.transform = CGAffineTransformIdentity;
127
157
  fromViewController.view.transform = rightTransform;
158
+ if (shadowView) {
159
+ shadowView.alpha = 0.0;
160
+ }
128
161
  };
129
162
  void (^completionBlock)(BOOL) = ^(BOOL finished) {
163
+ if (shadowView) {
164
+ [shadowView removeFromSuperview];
165
+ }
130
166
  fromViewController.view.transform = CGAffineTransformIdentity;
131
167
  toViewController.view.transform = CGAffineTransformIdentity;
132
168
  [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
@@ -381,12 +417,13 @@ static const float RNSFadeCloseDelayTransitionDurationProportion = 0.1 / 0.35;
381
417
  }
382
418
 
383
419
  - (void)animateTransitionWithStackAnimation:(RNSScreenStackAnimation)animation
420
+ shadowEnabled:(BOOL)shadowEnabled
384
421
  transitionContext:(id<UIViewControllerContextTransitioning>)transitionContext
385
422
  toVC:(UIViewController *)toVC
386
423
  fromVC:(UIViewController *)fromVC
387
424
  {
388
425
  if (animation == RNSScreenStackAnimationSimplePush) {
389
- [self animateSimplePushWithTransitionContext:transitionContext toVC:toVC fromVC:fromVC];
426
+ [self animateSimplePushWithShadowEnabled:shadowEnabled transitionContext:transitionContext toVC:toVC fromVC:fromVC];
390
427
  return;
391
428
  } else if (animation == RNSScreenStackAnimationSlideFromLeft) {
392
429
  [self animateSlideFromLeftWithTransitionContext:transitionContext toVC:toVC fromVC:fromVC];
@@ -402,7 +439,7 @@ static const float RNSFadeCloseDelayTransitionDurationProportion = 0.1 / 0.35;
402
439
  return;
403
440
  }
404
441
  // simple_push is the default custom animation
405
- [self animateSimplePushWithTransitionContext:transitionContext toVC:toVC fromVC:fromVC];
442
+ [self animateSimplePushWithShadowEnabled:shadowEnabled transitionContext:transitionContext toVC:toVC fromVC:fromVC];
406
443
  }
407
444
 
408
445
  @end
@@ -55,6 +55,7 @@
55
55
  @property (nonatomic) BOOL translucent;
56
56
  @property (nonatomic) BOOL backButtonInCustomView;
57
57
  @property (nonatomic) UISemanticContentAttribute direction;
58
+ @property (nonatomic) UINavigationItemBackButtonDisplayMode backButtonDisplayMode;
58
59
 
59
60
  + (void)willShowViewController:(UIViewController *)vc
60
61
  animated:(BOOL)animated
@@ -70,5 +71,6 @@
70
71
 
71
72
  + (UIBlurEffectStyle)UIBlurEffectStyle:(id)json;
72
73
  + (UISemanticContentAttribute)UISemanticContentAttribute:(id)json;
74
+ + (UINavigationItemBackButtonDisplayMode)UINavigationItemBackButtonDisplayMode:(id)json;
73
75
 
74
76
  @end
@@ -19,6 +19,7 @@
19
19
  #import <React/RCTFont.h>
20
20
  #import <React/RCTImageLoader.h>
21
21
  #import <React/RCTImageSource.h>
22
+ #import "RNSConvert.h"
22
23
  #import "RNSScreen.h"
23
24
  #import "RNSScreenStackHeaderConfig.h"
24
25
  #import "RNSSearchBar.h"
@@ -140,8 +141,8 @@ namespace react = facebook::react;
140
141
  nextVC = nav.topViewController;
141
142
  }
142
143
 
143
- // we want updates sent to the VC below modal too since it is also visible
144
- BOOL isPresentingVC = nextVC != nil && vc.presentedViewController == nextVC;
144
+ // we want updates sent to the VC directly below modal too since it is also visible
145
+ BOOL isPresentingVC = nextVC != nil && vc.presentedViewController == nextVC && vc == nav.topViewController;
145
146
 
146
147
  BOOL isInFullScreenModal = nav == nil && _screenView.stackPresentation == RNSScreenStackPresentationFullScreenModal;
147
148
  // if nav is nil, it means we can be in a fullScreen modal, so there is no nextVC, but we still want to update
@@ -493,8 +494,17 @@ namespace react = facebook::react;
493
494
  config.direction == UISemanticContentAttributeForceRightToLeft) &&
494
495
  // iOS 12 cancels swipe gesture when direction is changed. See #1091
495
496
  navctr.view.semanticContentAttribute != config.direction) {
497
+ // This is needed for swipe back gesture direction
496
498
  navctr.view.semanticContentAttribute = config.direction;
499
+
500
+ // This is responsible for the direction of the navigationBar and its contents
497
501
  navctr.navigationBar.semanticContentAttribute = config.direction;
502
+ [[UIButton appearanceWhenContainedInInstancesOfClasses:@[ navctr.navigationBar.class ]]
503
+ setSemanticContentAttribute:config.direction];
504
+ [[UIView appearanceWhenContainedInInstancesOfClasses:@[ navctr.navigationBar.class ]]
505
+ setSemanticContentAttribute:config.direction];
506
+ [[UISearchBar appearanceWhenContainedInInstancesOfClasses:@[ navctr.navigationBar.class ]]
507
+ setSemanticContentAttribute:config.direction];
498
508
  }
499
509
 
500
510
  if (shouldHide) {
@@ -511,8 +521,24 @@ namespace react = facebook::react;
511
521
  action:nil];
512
522
  [backBarButtonItem setMenuHidden:config.disableBackButtonMenu];
513
523
 
524
+ auto isBackButtonCustomized = !isBackTitleBlank || config.disableBackButtonMenu;
525
+
526
+ #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_14_0) && \
527
+ __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
528
+ if (@available(iOS 14.0, *)) {
529
+ prevItem.backButtonDisplayMode = config.backButtonDisplayMode;
530
+ }
531
+ #endif
532
+
514
533
  if (config.isBackTitleVisible) {
515
- if (config.backTitleFontFamily || config.backTitleFontSize) {
534
+ if ((config.backTitleFontFamily &&
535
+ // While being used by react-navigation, the `backTitleFontFamily` will
536
+ // be set to "System" by default - which is the system default font.
537
+ // To avoid always considering the font as customized, we need to have an additional check.
538
+ // See: https://github.com/software-mansion/react-native-screens/pull/2105#discussion_r1565222738
539
+ ![config.backTitleFontFamily isEqual:@"System"]) ||
540
+ config.backTitleFontSize) {
541
+ isBackButtonCustomized = YES;
516
542
  NSMutableDictionary *attrs = [NSMutableDictionary new];
517
543
  NSNumber *size = config.backTitleFontSize ?: @17;
518
544
  if (config.backTitleFontFamily) {
@@ -535,9 +561,17 @@ namespace react = facebook::react;
535
561
  // When backBarButtonItem's title is null, back menu will use value
536
562
  // of backButtonTitle
537
563
  [backBarButtonItem setTitle:nil];
564
+ isBackButtonCustomized = YES;
538
565
  prevItem.backButtonTitle = resolvedBackTitle;
539
566
  }
540
- prevItem.backBarButtonItem = backBarButtonItem;
567
+
568
+ // Prevent unnecessary assignment of backBarButtonItem if it is not customized,
569
+ // as assigning one will override the native behavior of automatically shortening
570
+ // the title to "Back" or hide the back title if there's not enough space.
571
+ // See: https://github.com/software-mansion/react-native-screens/issues/1589
572
+ if (isBackButtonCustomized) {
573
+ prevItem.backBarButtonItem = backBarButtonItem;
574
+ }
541
575
 
542
576
  if (@available(iOS 11.0, *)) {
543
577
  if (config.largeTitle) {
@@ -555,6 +589,12 @@ namespace react = facebook::react;
555
589
  navitem.standardAppearance = appearance;
556
590
  navitem.compactAppearance = appearance;
557
591
 
592
+ // appearance does not apply to the tvOS so we need to use lagacy customization
593
+ #if TARGET_OS_TV
594
+ navctr.navigationBar.titleTextAttributes = appearance.titleTextAttributes;
595
+ navctr.navigationBar.backgroundColor = appearance.backgroundColor;
596
+ #endif
597
+
558
598
  UINavigationBarAppearance *scrollEdgeAppearance =
559
599
  [[UINavigationBarAppearance alloc] initWithBarAppearance:appearance];
560
600
  if (config.largeTitleBackgroundColor != nil) {
@@ -581,8 +621,6 @@ namespace react = facebook::react;
581
621
  #endif
582
622
  }
583
623
  #if !TARGET_OS_TV
584
- // Workaround for the wrong rotation of back button arrow in RTL mode.
585
- navitem.hidesBackButton = true;
586
624
  navitem.hidesBackButton = config.hideBackButton;
587
625
  #endif
588
626
  navitem.leftBarButtonItem = nil;
@@ -590,6 +628,8 @@ namespace react = facebook::react;
590
628
  navitem.titleView = nil;
591
629
 
592
630
  for (RNSScreenStackHeaderSubview *subview in config.reactSubviews) {
631
+ // This code should be kept in sync on Fabric with analogous switch statement in
632
+ // `- [RNSScreenStackHeaderConfig replaceNavigationBarViewsWithSnapshotOfSubview:]` method.
593
633
  switch (subview.type) {
594
634
  case RNSScreenStackHeaderSubviewTypeLeft: {
595
635
  #if !TARGET_OS_TV
@@ -640,13 +680,6 @@ namespace react = facebook::react;
640
680
  }
641
681
  }
642
682
 
643
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0), dispatch_get_main_queue(), ^{
644
- // Position the contents in the navigation bar, regarding to the direction.
645
- for (UIView *view in navctr.navigationBar.subviews) {
646
- view.semanticContentAttribute = config.direction;
647
- }
648
- });
649
-
650
683
  // This assignment should be done after `navitem.titleView = ...` assignment (iOS 16.0 bug).
651
684
  // See: https://github.com/software-mansion/react-native-screens/issues/1570 (comments)
652
685
  navitem.title = config.title;
@@ -724,8 +757,38 @@ namespace react = facebook::react;
724
757
 
725
758
  - (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
726
759
  {
760
+ // For explanation of why we can make a snapshot here despite the fact that our children are already
761
+ // unmounted see https://github.com/software-mansion/react-native-screens/pull/2261
762
+ [self replaceNavigationBarViewsWithSnapshotOfSubview:(RNSScreenStackHeaderSubview *)childComponentView];
727
763
  [_reactSubviews removeObject:(RNSScreenStackHeaderSubview *)childComponentView];
728
764
  [childComponentView removeFromSuperview];
765
+ [self updateViewControllerIfNeeded];
766
+ }
767
+
768
+ - (void)replaceNavigationBarViewsWithSnapshotOfSubview:(RNSScreenStackHeaderSubview *)childComponentView
769
+ {
770
+ UINavigationItem *navitem = _screenView.controller.navigationItem;
771
+ UIView *snapshot = [childComponentView snapshotViewAfterScreenUpdates:NO];
772
+
773
+ // This code should be kept in sync with analogous switch statement in
774
+ // `+ [RNSScreenStackHeaderConfig updateViewController: withConfig: animated:]` method.
775
+ switch (childComponentView.type) {
776
+ case RNSScreenStackHeaderSubviewTypeLeft:
777
+ navitem.leftBarButtonItem.customView = snapshot;
778
+ break;
779
+ case RNSScreenStackHeaderSubviewTypeCenter:
780
+ case RNSScreenStackHeaderSubviewTypeTitle:
781
+ navitem.titleView = snapshot;
782
+ break;
783
+ case RNSScreenStackHeaderSubviewTypeRight:
784
+ navitem.rightBarButtonItem.customView = snapshot;
785
+ break;
786
+ case RNSScreenStackHeaderSubviewTypeSearchBar:
787
+ case RNSScreenStackHeaderSubviewTypeBackButton:
788
+ break;
789
+ default:
790
+ RCTLogError(@"[RNScreens] Unhandled subview type: %ld", childComponentView.type);
791
+ }
729
792
  }
730
793
 
731
794
  static RCTResizeMode resizeModeFromCppEquiv(react::ImageResizeMode resizeMode)
@@ -769,11 +832,6 @@ static RCTResizeMode resizeModeFromCppEquiv(react::ImageResizeMode resizeMode)
769
832
  _initialPropsSet = NO;
770
833
  }
771
834
 
772
- + (react::ComponentDescriptorProvider)componentDescriptorProvider
773
- {
774
- return react::concreteComponentDescriptorProvider<react::RNSScreenStackHeaderConfigComponentDescriptor>();
775
- }
776
-
777
835
  - (NSNumber *)getFontSizePropValue:(int)value
778
836
  {
779
837
  if (value > 0)
@@ -781,14 +839,9 @@ static RCTResizeMode resizeModeFromCppEquiv(react::ImageResizeMode resizeMode)
781
839
  return nil;
782
840
  }
783
841
 
784
- - (UISemanticContentAttribute)getDirectionPropValue:(react::RNSScreenStackHeaderConfigDirection)direction
842
+ + (react::ComponentDescriptorProvider)componentDescriptorProvider
785
843
  {
786
- switch (direction) {
787
- case react::RNSScreenStackHeaderConfigDirection::Rtl:
788
- return UISemanticContentAttributeForceRightToLeft;
789
- case react::RNSScreenStackHeaderConfigDirection::Ltr:
790
- return UISemanticContentAttributeForceLeftToRight;
791
- }
844
+ return react::concreteComponentDescriptorProvider<react::RNSScreenStackHeaderConfigComponentDescriptor>();
792
845
  }
793
846
 
794
847
  - (void)updateProps:(react::Props::Shared const &)props oldProps:(react::Props::Shared const &)oldProps
@@ -835,9 +888,11 @@ static RCTResizeMode resizeModeFromCppEquiv(react::ImageResizeMode resizeMode)
835
888
  _backTitleFontSize = [self getFontSizePropValue:newScreenProps.backTitleFontSize];
836
889
  _hideBackButton = newScreenProps.hideBackButton;
837
890
  _disableBackButtonMenu = newScreenProps.disableBackButtonMenu;
891
+ _backButtonDisplayMode =
892
+ [RNSConvert UINavigationItemBackButtonDisplayModeFromCppEquivalent:newScreenProps.backButtonDisplayMode];
838
893
 
839
894
  if (newScreenProps.direction != oldScreenProps.direction) {
840
- _direction = [self getDirectionPropValue:newScreenProps.direction];
895
+ _direction = [RNSConvert UISemanticContentAttributeFromCppEquivalent:newScreenProps.direction];
841
896
  }
842
897
 
843
898
  _backTitleVisible = newScreenProps.backTitleVisible;
@@ -928,8 +983,8 @@ RCT_EXPORT_VIEW_PROPERTY(hideBackButton, BOOL)
928
983
  RCT_EXPORT_VIEW_PROPERTY(hideShadow, BOOL)
929
984
  RCT_EXPORT_VIEW_PROPERTY(backButtonInCustomView, BOOL)
930
985
  RCT_EXPORT_VIEW_PROPERTY(disableBackButtonMenu, BOOL)
931
- // `hidden` is an UIView property, we need to use different name internally
932
- RCT_REMAP_VIEW_PROPERTY(hidden, hide, BOOL)
986
+ RCT_EXPORT_VIEW_PROPERTY(backButtonDisplayMode, UINavigationItemBackButtonDisplayMode)
987
+ RCT_REMAP_VIEW_PROPERTY(hidden, hide, BOOL) // `hidden` is an UIView property, we need to use different name internally
933
988
  RCT_EXPORT_VIEW_PROPERTY(translucent, BOOL)
934
989
 
935
990
  @end
@@ -985,6 +1040,16 @@ RCT_ENUM_CONVERTER(
985
1040
  UISemanticContentAttributeUnspecified,
986
1041
  integerValue)
987
1042
 
1043
+ RCT_ENUM_CONVERTER(
1044
+ UINavigationItemBackButtonDisplayMode,
1045
+ (@{
1046
+ @"default" : @(UINavigationItemBackButtonDisplayModeDefault),
1047
+ @"generic" : @(UINavigationItemBackButtonDisplayModeGeneric),
1048
+ @"minimal" : @(UINavigationItemBackButtonDisplayModeMinimal),
1049
+ }),
1050
+ UINavigationItemBackButtonDisplayModeDefault,
1051
+ integerValue)
1052
+
988
1053
  RCT_ENUM_CONVERTER(UIBlurEffectStyle, ([self blurEffectsForIOSVersion]), UIBlurEffectStyleExtraLight, integerValue)
989
1054
 
990
1055
  @end
@@ -72,9 +72,17 @@ namespace react = facebook::react;
72
72
  self);
73
73
  } else {
74
74
  self.bounds = CGRect{CGPointZero, frame.size};
75
+ // We're forcing the parent view to layout this subview with correct frame size,
76
+ // see: https://github.com/software-mansion/react-native-screens/pull/2248
77
+ [self.superview layoutIfNeeded];
75
78
  }
76
79
  }
77
80
 
81
+ + (BOOL)shouldBeRecycled
82
+ {
83
+ return NO;
84
+ }
85
+
78
86
  #else
79
87
  #pragma mark - Paper specific
80
88
 
@@ -29,11 +29,11 @@
29
29
 
30
30
  #ifdef RCT_NEW_ARCH_ENABLED
31
31
  #else
32
- @property (nonatomic, copy) RCTBubblingEventBlock onChangeText;
33
- @property (nonatomic, copy) RCTBubblingEventBlock onCancelButtonPress;
34
- @property (nonatomic, copy) RCTBubblingEventBlock onSearchButtonPress;
35
- @property (nonatomic, copy) RCTBubblingEventBlock onFocus;
36
- @property (nonatomic, copy) RCTBubblingEventBlock onBlur;
32
+ @property (nonatomic, copy) RCTDirectEventBlock onChangeText;
33
+ @property (nonatomic, copy) RCTDirectEventBlock onCancelButtonPress;
34
+ @property (nonatomic, copy) RCTDirectEventBlock onSearchButtonPress;
35
+ @property (nonatomic, copy) RCTDirectEventBlock onSearchFocus;
36
+ @property (nonatomic, copy) RCTDirectEventBlock onSearchBlur;
37
37
  #endif
38
38
 
39
39
  @end
@@ -67,11 +67,11 @@ namespace react = facebook::react;
67
67
  #ifdef RCT_NEW_ARCH_ENABLED
68
68
  if (_eventEmitter != nullptr) {
69
69
  std::dynamic_pointer_cast<const react::RNSSearchBarEventEmitter>(_eventEmitter)
70
- ->onFocus(react::RNSSearchBarEventEmitter::OnFocus{});
70
+ ->onSearchFocus(react::RNSSearchBarEventEmitter::OnSearchFocus{});
71
71
  }
72
72
  #else
73
- if (self.onFocus) {
74
- self.onFocus(@{});
73
+ if (self.onSearchFocus) {
74
+ self.onSearchFocus(@{});
75
75
  }
76
76
  #endif
77
77
  }
@@ -81,11 +81,11 @@ namespace react = facebook::react;
81
81
  #ifdef RCT_NEW_ARCH_ENABLED
82
82
  if (_eventEmitter != nullptr) {
83
83
  std::dynamic_pointer_cast<const react::RNSSearchBarEventEmitter>(_eventEmitter)
84
- ->onBlur(react::RNSSearchBarEventEmitter::OnBlur{});
84
+ ->onSearchBlur(react::RNSSearchBarEventEmitter::OnSearchBlur{});
85
85
  }
86
86
  #else
87
- if (self.onBlur) {
88
- self.onBlur(@{});
87
+ if (self.onSearchBlur) {
88
+ self.onSearchBlur(@{});
89
89
  }
90
90
  #endif
91
91
  }
@@ -414,11 +414,11 @@ RCT_EXPORT_VIEW_PROPERTY(textColor, UIColor)
414
414
  RCT_EXPORT_VIEW_PROPERTY(cancelButtonText, NSString)
415
415
  RCT_EXPORT_VIEW_PROPERTY(placement, RNSSearchBarPlacement)
416
416
 
417
- RCT_EXPORT_VIEW_PROPERTY(onChangeText, RCTBubblingEventBlock)
418
- RCT_EXPORT_VIEW_PROPERTY(onCancelButtonPress, RCTBubblingEventBlock)
419
- RCT_EXPORT_VIEW_PROPERTY(onSearchButtonPress, RCTBubblingEventBlock)
420
- RCT_EXPORT_VIEW_PROPERTY(onFocus, RCTBubblingEventBlock)
421
- RCT_EXPORT_VIEW_PROPERTY(onBlur, RCTBubblingEventBlock)
417
+ RCT_EXPORT_VIEW_PROPERTY(onChangeText, RCTDirectEventBlock)
418
+ RCT_EXPORT_VIEW_PROPERTY(onCancelButtonPress, RCTDirectEventBlock)
419
+ RCT_EXPORT_VIEW_PROPERTY(onSearchButtonPress, RCTDirectEventBlock)
420
+ RCT_EXPORT_VIEW_PROPERTY(onSearchFocus, RCTDirectEventBlock)
421
+ RCT_EXPORT_VIEW_PROPERTY(onSearchBlur, RCTDirectEventBlock)
422
422
 
423
423
  #ifndef RCT_NEW_ARCH_ENABLED
424
424
 
@@ -0,0 +1,15 @@
1
+ #ifdef RCT_NEW_ARCH_ENABLED
2
+
3
+ #import <React/RCTSurfaceTouchHandler.h>
4
+
5
+ NS_ASSUME_NONNULL_BEGIN
6
+
7
+ @interface RCTSurfaceTouchHandler (RNSUtility)
8
+
9
+ - (void)rnscreens_cancelTouches;
10
+
11
+ @end
12
+
13
+ NS_ASSUME_NONNULL_END
14
+
15
+ #endif // RCT_NEW_ARCH_ENABLED
@@ -0,0 +1,14 @@
1
+ #ifdef RCT_NEW_ARCH_ENABLED
2
+ #import "RCTSurfaceTouchHandler+RNSUtility.h"
3
+
4
+ @implementation RCTSurfaceTouchHandler (RNSUtility)
5
+
6
+ - (void)rnscreens_cancelTouches
7
+ {
8
+ [self setEnabled:NO];
9
+ [self setEnabled:YES];
10
+ [self reset];
11
+ }
12
+
13
+ @end
14
+ #endif // RCT_NEW_ARCH_ENABLED
@@ -0,0 +1,15 @@
1
+ #ifndef RCT_NEW_ARCH_ENABLED
2
+
3
+ #import <React/RCTTouchHandler.h>
4
+
5
+ NS_ASSUME_NONNULL_BEGIN
6
+
7
+ @interface RCTTouchHandler (RNSUtility)
8
+
9
+ - (void)rnscreens_cancelTouches;
10
+
11
+ @end
12
+
13
+ NS_ASSUME_NONNULL_END
14
+
15
+ #endif // !RCT_NEW_ARCH_ENABLED
@@ -0,0 +1,15 @@
1
+ #ifndef RCT_NEW_ARCH_ENABLED
2
+ #import "RCTTouchHandler+RNSUtility.h"
3
+
4
+ @implementation RCTTouchHandler (RNSUtility)
5
+
6
+ - (void)rnscreens_cancelTouches
7
+ {
8
+ [self setEnabled:NO];
9
+ [self setEnabled:YES];
10
+ [self reset];
11
+ }
12
+
13
+ @end
14
+
15
+ #endif // !RCT_NEW_ARCH_ENABLED
@@ -0,0 +1,23 @@
1
+ #import <Foundation/Foundation.h>
2
+
3
+ #ifdef RCT_NEW_ARCH_ENABLED
4
+ #import <React/RCTSurfaceTouchHandler.h>
5
+ #else
6
+ #import <React/RCTTouchHandler.h>
7
+ #endif // RCT_NEW_ARCH_ENABLED
8
+
9
+ #ifdef RCT_NEW_ARCH_ENABLED
10
+ #define RNS_TOUCH_HANDLER_ARCH_TYPE RCTSurfaceTouchHandler
11
+ #else
12
+ #define RNS_TOUCH_HANDLER_ARCH_TYPE RCTTouchHandler
13
+ #endif // RCT_NEW_ARCH_ENABLED
14
+
15
+ NS_ASSUME_NONNULL_BEGIN
16
+
17
+ @interface UIView (RNSUtility)
18
+
19
+ - (nullable RNS_TOUCH_HANDLER_ARCH_TYPE *)rnscreens_findTouchHandlerInAncestorChain;
20
+
21
+ @end
22
+
23
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,55 @@
1
+
2
+ #import "UIView+RNSUtility.h"
3
+
4
+ #ifdef RCT_NEW_ARCH_ENABLED
5
+ #import <React/RCTSurfaceView.h>
6
+ #endif
7
+
8
+ @implementation UIView (RNSUtility)
9
+
10
+ - (nullable RNS_TOUCH_HANDLER_ARCH_TYPE *)rnscreens_findTouchHandlerInAncestorChain
11
+ {
12
+ UIView *parent = self.superview;
13
+
14
+ #ifdef RCT_NEW_ARCH_ENABLED
15
+ // On Fabric there is no view that exposes touchHandler above us in the view hierarchy, however it is still
16
+ // utilised. `RCTSurfaceView` should be present above us, which hosts `RCTFabricSurface` instance, which in turn
17
+ // hosts `RCTSurfaceTouchHandler` as a private field. When initialised, `RCTSurfaceTouchHandler` is attached to the
18
+ // surface view as a gestureRecognizer <- and this is where we can lay our hands on it.
19
+
20
+ while (parent != nil && ![parent isKindOfClass:RCTSurfaceView.class]) {
21
+ parent = parent.superview;
22
+ }
23
+
24
+ // This could be possible in modal context
25
+ if (parent == nil) {
26
+ return nil;
27
+ }
28
+
29
+ // Experimentation shows that RCTSurfaceTouchHandler is the only gestureRecognizer registered here,
30
+ // so we should not be afraid of any performance hit here.
31
+ for (UIGestureRecognizer *recognizer in parent.gestureRecognizers) {
32
+ if ([recognizer isKindOfClass:RCTSurfaceTouchHandler.class]) {
33
+ return static_cast<RNS_TOUCH_HANDLER_ARCH_TYPE *>(recognizer);
34
+ }
35
+ }
36
+
37
+ #else
38
+
39
+ // On Paper we can access touchHandler hosted by `RCTRootContentView` which should be above ScreenStack
40
+ // in view hierarchy.
41
+ while (parent != nil && ![parent respondsToSelector:@selector(touchHandler)]) {
42
+ parent = parent.superview;
43
+ }
44
+
45
+ if (parent != nil) {
46
+ RCTTouchHandler *touchHandler = [parent performSelector:@selector(touchHandler)];
47
+ return static_cast<RNS_TOUCH_HANDLER_ARCH_TYPE *>(touchHandler);
48
+ }
49
+
50
+ #endif // RCT_NEW_ARCH_ENABLED
51
+
52
+ return nil;
53
+ }
54
+
55
+ @end