react-native-screens 5.0.0-alpha.3 → 5.0.0-alpha.4

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 (337) hide show
  1. package/README.md +0 -4
  2. package/android/build.gradle +1 -0
  3. package/android/consumer-rules.pro +3 -0
  4. package/android/src/fabric/java/com/swmansion/rnscreens/legacy/FabricEnabledHeaderConfigViewGroup.kt +2 -0
  5. package/android/src/fabric/java/com/swmansion/rnscreens/legacy/FabricEnabledHeaderSubviewViewGroup.kt +2 -0
  6. package/android/src/fabric/java/com/swmansion/rnscreens/legacy/FabricEnabledViewGroup.kt +18 -3
  7. package/android/src/main/java/com/swmansion/rnscreens/RNScreensPackage.kt +2 -2
  8. package/android/src/main/java/com/swmansion/rnscreens/ScreensModule.kt +7 -3
  9. package/android/src/main/java/com/swmansion/rnscreens/common/colorscheme/ColorSchemeCoordinator.kt +11 -1
  10. package/android/src/main/java/com/swmansion/rnscreens/common/container/Container.kt +9 -0
  11. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/coordinator/FormSheetAppearanceCoordinator.kt +2 -2
  12. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/coordinator/FormSheetBehaviorController.kt +2 -2
  13. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/coordinator/FormSheetDimensionsCoordinator.kt +6 -6
  14. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/coordinator/FormSheetKeyboardCoordinator.kt +1 -1
  15. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/coordinator/FormSheetNativeDismissCoordinator.kt +2 -2
  16. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native/core → core/modals/formsheet/dialog}/FormSheetAvailableHeightProvider.kt +1 -1
  17. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native/core → core/modals/formsheet/dialog}/FormSheetContainer.kt +1 -1
  18. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native/core → core/modals/formsheet/dialog}/FormSheetDialog.kt +1 -1
  19. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native/core → core/modals/formsheet/dialog}/FormSheetDialogManager.kt +26 -8
  20. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/interfaces/FormSheetContentSizeChangeDelegate.kt +1 -1
  21. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/interfaces/FormSheetContentSizeChangeProvider.kt +1 -1
  22. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/interfaces/FormSheetDialogEventEmitter.kt +1 -1
  23. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/model/FormSheetConfig.kt +1 -1
  24. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/model/FormSheetDetents.kt +1 -1
  25. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/presentation/FormSheetAnimatorFactory.kt +1 -1
  26. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/presentation/FormSheetDimmingManager.kt +6 -18
  27. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/presentation/FormSheetDismissalOrigin.kt +1 -1
  28. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/presentation/FormSheetPresentation.kt +10 -10
  29. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/presentation/FormSheetPresentationManager.kt +1 -1
  30. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/presentation/FormSheetPresentationState.kt +1 -1
  31. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/presentation/FormSheetStackRegistry.kt +1 -1
  32. package/android/src/main/java/com/swmansion/rnscreens/ext/FragmentManagerExt.kt +6 -0
  33. package/android/src/main/java/com/swmansion/rnscreens/helpers/FragmentManagerHelper.kt +0 -3
  34. package/android/src/main/java/com/swmansion/rnscreens/helpers/NoTintDrawable.kt +31 -0
  35. package/android/src/main/java/com/swmansion/rnscreens/helpers/ViewFinder.kt +0 -17
  36. package/android/src/main/java/com/swmansion/rnscreens/legacy/Screen.kt +29 -2
  37. package/android/src/main/java/com/swmansion/rnscreens/legacy/ScreenContainer.kt +35 -3
  38. package/android/src/main/java/com/swmansion/rnscreens/legacy/ScreenStack.kt +13 -0
  39. package/android/src/main/java/com/swmansion/rnscreens/legacy/ScreenStackHeaderConfig.kt +7 -1
  40. package/android/src/main/java/com/swmansion/rnscreens/legacy/ScreenStackHeaderSubviewManager.kt +7 -0
  41. package/android/src/main/java/com/swmansion/rnscreens/legacy/ScreenStackViewManager.kt +6 -0
  42. package/android/src/main/java/com/swmansion/rnscreens/legacy/bottomsheet/DimmingView.kt +2 -0
  43. package/android/src/main/java/com/swmansion/rnscreens/legacy/stack/views/ScreensCoordinatorLayout.kt +2 -0
  44. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/contentwrapper/FormSheetContentWrapper.kt +3 -3
  45. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/contentwrapper/FormSheetContentWrapperViewManager.kt +1 -1
  46. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/event/FormSheetDetentChangedEvent.kt +1 -1
  47. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/event/FormSheetDidAppearEvent.kt +1 -1
  48. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/event/FormSheetDidDisappearEvent.kt +1 -1
  49. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/event/FormSheetDismissEvent.kt +1 -1
  50. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/event/FormSheetNativeDismissEvent.kt +1 -1
  51. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/event/FormSheetNativeDismissPreventedEvent.kt +1 -1
  52. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/event/FormSheetSyncFlushEvent.kt +1 -1
  53. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/event/FormSheetWillAppearEvent.kt +1 -1
  54. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/event/FormSheetWillDisappearEvent.kt +1 -1
  55. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/host/FormSheetContentView.kt +3 -3
  56. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/host/FormSheetHost.kt +5 -3
  57. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/host/FormSheetHostEventEmitter.kt +11 -11
  58. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/host/FormSheetHostViewManager.kt +10 -10
  59. package/android/src/main/java/com/swmansion/rnscreens/safearea/OnInterfaceInsetsChangeListener.kt +15 -0
  60. package/android/src/main/java/com/swmansion/rnscreens/safearea/SafeAreaProvider.kt +8 -8
  61. package/android/src/main/java/com/swmansion/rnscreens/safearea/SafeAreaView.kt +3 -2
  62. package/android/src/main/java/com/swmansion/rnscreens/stack/header/StackHeaderApplicator.kt +0 -52
  63. package/android/src/main/java/com/swmansion/rnscreens/stack/header/StackHeaderCoordinatorLayout.kt +112 -59
  64. package/android/src/main/java/com/swmansion/rnscreens/stack/header/appbar/StackHeaderAppBarLayout.kt +1 -1
  65. package/android/src/main/java/com/swmansion/rnscreens/stack/header/appbar/StackHeaderToolbar.kt +74 -0
  66. package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderConfig.kt +4 -73
  67. package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderConfigurationObserver.kt +5 -2
  68. package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderConfigurationProviding.kt +0 -6
  69. package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderInvalidationFlags.kt +3 -7
  70. package/android/src/main/java/com/swmansion/rnscreens/stack/host/FragmentOperation.kt +1 -0
  71. package/android/src/main/java/com/swmansion/rnscreens/stack/host/FragmentOperationExecutor.kt +2 -1
  72. package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackContainer.kt +57 -19
  73. package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackContainerDelegate.kt +7 -0
  74. package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackContainerUpdateCoordinator.kt +5 -3
  75. package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackHost.kt +14 -5
  76. package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackUpdateBatchStateProviding.kt +10 -0
  77. package/android/src/main/java/com/swmansion/rnscreens/stack/screen/PreventNativeDismissCallback.kt +2 -2
  78. package/android/src/main/java/com/swmansion/rnscreens/stack/screen/StackScreenFragment.kt +33 -12
  79. package/android/src/main/java/com/swmansion/rnscreens/tabs/appearance/TabsAppearanceApplicator.kt +106 -10
  80. package/android/src/main/java/com/swmansion/rnscreens/tabs/appearance/TabsAppearanceCoordinator.kt +44 -3
  81. package/android/src/main/java/com/swmansion/rnscreens/tabs/appearance/TabsAppearanceModel.kt +3 -0
  82. package/android/src/main/java/com/swmansion/rnscreens/tabs/container/CustomBottomNavigationView.kt +21 -1
  83. package/android/src/main/java/com/swmansion/rnscreens/tabs/container/TabsContainer.kt +28 -13
  84. package/android/src/main/java/com/swmansion/rnscreens/tabs/container/TabsContainerDelegate.kt +11 -0
  85. package/android/src/main/java/com/swmansion/rnscreens/tabs/host/TabsHost.kt +12 -2
  86. package/android/src/main/java/com/swmansion/rnscreens/tabs/screen/TabsScreen.kt +41 -17
  87. package/android/src/main/java/com/swmansion/rnscreens/tabs/screen/TabsScreenDelegate.kt +6 -0
  88. package/android/src/main/java/com/swmansion/rnscreens/tabs/screen/TabsScreenIcon.kt +82 -0
  89. package/android/src/main/java/com/swmansion/rnscreens/tabs/screen/TabsScreenIconTinting.kt +17 -0
  90. package/android/src/main/java/com/swmansion/rnscreens/tabs/screen/TabsScreenViewManager.kt +26 -19
  91. package/android/src/main/java/com/swmansion/rnscreens/utils/DimensionUtils.kt +14 -7
  92. package/android/src/main/java/com/swmansion/rnscreens/utils/RNSLog.kt +22 -6
  93. package/ios/RNSEnums.h +14 -1
  94. package/ios/helpers/image/RNSImageLoadingHelper.h +7 -0
  95. package/ios/helpers/image/RNSImageLoadingHelper.mm +9 -0
  96. package/ios/legacy/RNSBarButtonItem.mm +15 -0
  97. package/ios/legacy/RNSConvert.h +6 -0
  98. package/ios/legacy/RNSConvert.mm +17 -0
  99. package/ios/legacy/RNSNavigationBar.h +31 -0
  100. package/ios/legacy/RNSNavigationBar.mm +15 -0
  101. package/ios/legacy/RNSScreen.mm +122 -12
  102. package/ios/legacy/RNSScreenContentWrapper.mm +4 -1
  103. package/ios/legacy/RNSScreenStack.mm +202 -15
  104. package/ios/legacy/RNSScreenStackHeaderConfig.h +30 -0
  105. package/ios/legacy/RNSScreenStackHeaderConfig.mm +70 -17
  106. package/ios/legacy/RNSScreenStackHeaderSubview.mm +33 -0
  107. package/ios/safe-area/RNSSafeAreaViewComponentView.mm +1 -12
  108. package/ios/stack/conversion/RNSConversions-Stack.h +30 -0
  109. package/ios/stack/conversion/RNSConversions-Stack.mm +79 -0
  110. package/ios/stack/header/RNSHeaderItemVisibilityPriority.h +9 -0
  111. package/ios/stack/header/RNSStackHeaderConfigComponentView.h +4 -0
  112. package/ios/stack/header/RNSStackHeaderConfigComponentView.mm +40 -6
  113. package/ios/stack/header/RNSStackHeaderConfigDataProviding.h +5 -0
  114. package/ios/stack/header/RNSStackHeaderContentFactory.mm +3 -0
  115. package/ios/stack/header/RNSStackHeaderItemComponentView.h +9 -2
  116. package/ios/stack/header/RNSStackHeaderItemComponentView.mm +57 -3
  117. package/ios/stack/header/RNSStackHeaderItemDataProviding.h +5 -0
  118. package/ios/stack/header/RNSStackHeaderMenuCoordinator.h +13 -0
  119. package/ios/stack/header/RNSStackHeaderMenuCoordinator.mm +24 -0
  120. package/ios/stack/header/RNSStackHeaderMenuFinder.h +8 -1
  121. package/ios/stack/header/RNSStackHeaderMenuFinder.mm +26 -9
  122. package/ios/stack/screen/RNSStackScreenComponentView.h +2 -1
  123. package/ios/stack/screen/RNSStackScreenComponentView.mm +23 -0
  124. package/ios/stack/screen/RNSStackScreenHeaderCoordinator.mm +81 -8
  125. package/ios/tabs/RNSTabBarAppearanceCoordinator.mm +79 -16
  126. package/ios/tabs/RNSTabBarItemCoordinator.h +16 -0
  127. package/ios/tabs/RNSTabBarItemCoordinator.mm +143 -0
  128. package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryWrapperView.h +24 -0
  129. package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryWrapperView.mm +22 -0
  130. package/ios/tabs/conversion/RNSConversions-Tabs.h +22 -0
  131. package/ios/tabs/conversion/RNSConversions-Tabs.mm +97 -3
  132. package/ios/tabs/host/RNSTabBarController.h +16 -0
  133. package/ios/tabs/host/RNSTabBarController.mm +516 -107
  134. package/ios/tabs/host/RNSTabsHostComponentView.h +2 -0
  135. package/ios/tabs/host/RNSTabsHostComponentView.mm +32 -2
  136. package/ios/tabs/screen/RNSTabsScreenComponentView.h +21 -0
  137. package/ios/tabs/screen/RNSTabsScreenComponentView.mm +54 -69
  138. package/ios/tabs/screen/RNSTabsScreenViewController.mm +1 -0
  139. package/ios/utils/RNSDefines.h +12 -7
  140. package/lib/commonjs/components/stack/header/StackHeaderConfig.android.js +3 -2
  141. package/lib/commonjs/components/stack/header/StackHeaderConfig.android.js.map +1 -1
  142. package/lib/commonjs/components/stack/header/StackHeaderConfig.ios.js +8 -3
  143. package/lib/commonjs/components/stack/header/StackHeaderConfig.ios.js.map +1 -1
  144. package/lib/commonjs/components/stack/header/StackHeaderConfig.js +17 -0
  145. package/lib/commonjs/components/stack/header/StackHeaderConfig.js.map +1 -0
  146. package/lib/commonjs/components/stack/header/ios/StackHeaderItem.ios.js +4 -1
  147. package/lib/commonjs/components/stack/header/ios/StackHeaderItem.ios.js.map +1 -1
  148. package/lib/commonjs/components/stack/host/StackHost.js +8 -1
  149. package/lib/commonjs/components/stack/host/StackHost.js.map +1 -1
  150. package/lib/commonjs/components/stack/host/StackHost.web.js +0 -2
  151. package/lib/commonjs/components/stack/host/StackHost.web.js.map +1 -1
  152. package/lib/commonjs/components/stack/index.js.map +1 -1
  153. package/lib/commonjs/components/tabs/host/TabsHost.ios.js +2 -0
  154. package/lib/commonjs/components/tabs/host/TabsHost.ios.js.map +1 -1
  155. package/lib/commonjs/components/tabs/host/{TabsHost.web.js → TabsHost.js} +1 -1
  156. package/lib/commonjs/components/tabs/host/TabsHost.js.map +1 -0
  157. package/lib/commonjs/components/tabs/index.js.map +1 -1
  158. package/lib/commonjs/components/tabs/screen/TabsScreen.android.js +3 -1
  159. package/lib/commonjs/components/tabs/screen/TabsScreen.android.js.map +1 -1
  160. package/lib/commonjs/components/tabs/screen/TabsScreen.ios.js +10 -1
  161. package/lib/commonjs/components/tabs/screen/TabsScreen.ios.js.map +1 -1
  162. package/lib/commonjs/components/tabs/screen/{TabsScreen.web.js → TabsScreen.js} +1 -1
  163. package/lib/commonjs/components/tabs/screen/TabsScreen.js.map +1 -0
  164. package/lib/commonjs/components/{stack/header/StackHeaderConfig.d.js → tabs/screen/TabsScreenIcon.types.js} +1 -1
  165. package/lib/commonjs/components/{stack/header/StackHeaderConfig.d.js.map → tabs/screen/TabsScreenIcon.types.js.map} +1 -1
  166. package/lib/commonjs/fabric/legacy/ScreenStackHeaderSubviewNativeComponent.js.map +1 -1
  167. package/lib/commonjs/fabric/legacy/ScreenStackNativeComponent.js.map +1 -1
  168. package/lib/commonjs/fabric/stack/StackHeaderConfigIOSNativeComponent.js.map +1 -1
  169. package/lib/commonjs/fabric/stack/StackHeaderItemIOSNativeComponent.js.map +1 -1
  170. package/lib/commonjs/fabric/tabs/TabsHostIOSNativeComponent.js.map +1 -1
  171. package/lib/commonjs/fabric/tabs/TabsScreenAndroidNativeComponent.js.map +1 -1
  172. package/lib/commonjs/fabric/tabs/TabsScreenIOSNativeComponent.js +1 -1
  173. package/lib/commonjs/fabric/tabs/TabsScreenIOSNativeComponent.js.map +1 -1
  174. package/lib/commonjs/legacy/components/ScreenStackItem.js +31 -11
  175. package/lib/commonjs/legacy/components/ScreenStackItem.js.map +1 -1
  176. package/lib/module/components/stack/header/StackHeaderConfig.android.js +4 -3
  177. package/lib/module/components/stack/header/StackHeaderConfig.android.js.map +1 -1
  178. package/lib/module/components/stack/header/StackHeaderConfig.ios.js +9 -4
  179. package/lib/module/components/stack/header/StackHeaderConfig.ios.js.map +1 -1
  180. package/lib/module/components/stack/header/StackHeaderConfig.js +10 -0
  181. package/lib/module/components/stack/header/StackHeaderConfig.js.map +1 -0
  182. package/lib/module/components/stack/header/ios/StackHeaderItem.ios.js +4 -1
  183. package/lib/module/components/stack/header/ios/StackHeaderItem.ios.js.map +1 -1
  184. package/lib/module/components/stack/host/StackHost.js +9 -2
  185. package/lib/module/components/stack/host/StackHost.js.map +1 -1
  186. package/lib/module/components/stack/host/StackHost.web.js +0 -3
  187. package/lib/module/components/stack/host/StackHost.web.js.map +1 -1
  188. package/lib/module/components/stack/index.js.map +1 -1
  189. package/lib/module/components/tabs/host/TabsHost.ios.js +2 -0
  190. package/lib/module/components/tabs/host/TabsHost.ios.js.map +1 -1
  191. package/{src/components/tabs/host/TabsHost.web.tsx → lib/module/components/tabs/host/TabsHost.js} +1 -2
  192. package/lib/module/components/tabs/host/TabsHost.js.map +1 -0
  193. package/lib/module/components/tabs/index.js.map +1 -1
  194. package/lib/module/components/tabs/screen/TabsScreen.android.js +3 -1
  195. package/lib/module/components/tabs/screen/TabsScreen.android.js.map +1 -1
  196. package/lib/module/components/tabs/screen/TabsScreen.ios.js +10 -1
  197. package/lib/module/components/tabs/screen/TabsScreen.ios.js.map +1 -1
  198. package/{src/components/tabs/screen/TabsScreen.web.tsx → lib/module/components/tabs/screen/TabsScreen.js} +1 -2
  199. package/lib/module/components/tabs/screen/TabsScreen.js.map +1 -0
  200. package/lib/module/components/tabs/screen/TabsScreenIcon.types.js +2 -0
  201. package/lib/module/components/{stack/header/StackHeaderConfig.d.js.map → tabs/screen/TabsScreenIcon.types.js.map} +1 -1
  202. package/lib/module/fabric/legacy/ScreenStackHeaderSubviewNativeComponent.js.map +1 -1
  203. package/lib/module/fabric/legacy/ScreenStackNativeComponent.js.map +1 -1
  204. package/lib/module/fabric/stack/StackHeaderConfigIOSNativeComponent.js.map +1 -1
  205. package/lib/module/fabric/stack/StackHeaderItemIOSNativeComponent.js.map +1 -1
  206. package/lib/module/fabric/tabs/TabsHostIOSNativeComponent.js.map +1 -1
  207. package/lib/module/fabric/tabs/TabsScreenAndroidNativeComponent.js.map +1 -1
  208. package/lib/module/fabric/tabs/TabsScreenIOSNativeComponent.js +1 -1
  209. package/lib/module/fabric/tabs/TabsScreenIOSNativeComponent.js.map +1 -1
  210. package/lib/module/legacy/components/ScreenStackItem.js +31 -11
  211. package/lib/module/legacy/components/ScreenStackItem.js.map +1 -1
  212. package/lib/typescript/components/stack/header/StackHeaderConfig.android.d.ts +6 -3
  213. package/lib/typescript/components/stack/header/StackHeaderConfig.android.d.ts.map +1 -1
  214. package/lib/typescript/components/stack/header/StackHeaderConfig.android.types.d.ts +4 -3
  215. package/lib/typescript/components/stack/header/StackHeaderConfig.android.types.d.ts.map +1 -1
  216. package/lib/typescript/components/stack/header/StackHeaderConfig.d.ts +8 -0
  217. package/lib/typescript/components/stack/header/StackHeaderConfig.d.ts.map +1 -0
  218. package/lib/typescript/components/stack/header/StackHeaderConfig.ios.d.ts +6 -3
  219. package/lib/typescript/components/stack/header/StackHeaderConfig.ios.d.ts.map +1 -1
  220. package/lib/typescript/components/stack/header/StackHeaderConfig.ios.types.d.ts +137 -2
  221. package/lib/typescript/components/stack/header/StackHeaderConfig.ios.types.d.ts.map +1 -1
  222. package/lib/typescript/components/stack/header/StackHeaderConfig.types.d.ts +2 -0
  223. package/lib/typescript/components/stack/header/StackHeaderConfig.types.d.ts.map +1 -1
  224. package/lib/typescript/components/stack/header/ios/StackHeaderItem.ios.d.ts.map +1 -1
  225. package/lib/typescript/components/stack/header/ios/StackHeaderItem.ios.types.d.ts +5 -0
  226. package/lib/typescript/components/stack/header/ios/StackHeaderItem.ios.types.d.ts.map +1 -1
  227. package/lib/typescript/components/stack/host/StackHost.d.ts.map +1 -1
  228. package/lib/typescript/components/stack/host/StackHost.types.d.ts +24 -1
  229. package/lib/typescript/components/stack/host/StackHost.types.d.ts.map +1 -1
  230. package/lib/typescript/components/stack/host/StackHost.web.d.ts +1 -3
  231. package/lib/typescript/components/stack/host/StackHost.web.d.ts.map +1 -1
  232. package/lib/typescript/components/stack/index.d.ts +4 -3
  233. package/lib/typescript/components/stack/index.d.ts.map +1 -1
  234. package/lib/typescript/components/tabs/host/TabsHost.d.ts +5 -0
  235. package/lib/typescript/components/tabs/host/TabsHost.d.ts.map +1 -0
  236. package/lib/typescript/components/tabs/host/TabsHost.ios.d.ts.map +1 -1
  237. package/lib/typescript/components/tabs/host/TabsHost.ios.types.d.ts +56 -0
  238. package/lib/typescript/components/tabs/host/TabsHost.ios.types.d.ts.map +1 -1
  239. package/lib/typescript/components/tabs/index.d.ts +4 -6
  240. package/lib/typescript/components/tabs/index.d.ts.map +1 -1
  241. package/lib/typescript/components/tabs/screen/TabsScreen.android.d.ts.map +1 -1
  242. package/lib/typescript/components/tabs/screen/TabsScreen.android.types.d.ts +42 -7
  243. package/lib/typescript/components/tabs/screen/TabsScreen.android.types.d.ts.map +1 -1
  244. package/lib/typescript/components/tabs/screen/TabsScreen.d.ts +5 -0
  245. package/lib/typescript/components/tabs/screen/TabsScreen.d.ts.map +1 -0
  246. package/lib/typescript/components/tabs/screen/TabsScreen.ios.d.ts.map +1 -1
  247. package/lib/typescript/components/tabs/screen/TabsScreen.ios.types.d.ts +78 -13
  248. package/lib/typescript/components/tabs/screen/TabsScreen.ios.types.d.ts.map +1 -1
  249. package/lib/typescript/components/tabs/screen/TabsScreenIcon.types.d.ts +91 -0
  250. package/lib/typescript/components/tabs/screen/TabsScreenIcon.types.d.ts.map +1 -0
  251. package/lib/typescript/components/tabs/screen/index.d.ts +1 -0
  252. package/lib/typescript/components/tabs/screen/index.d.ts.map +1 -1
  253. package/lib/typescript/fabric/legacy/ScreenStackHeaderSubviewNativeComponent.d.ts +2 -0
  254. package/lib/typescript/fabric/legacy/ScreenStackHeaderSubviewNativeComponent.d.ts.map +1 -1
  255. package/lib/typescript/fabric/legacy/ScreenStackNativeComponent.d.ts +1 -0
  256. package/lib/typescript/fabric/legacy/ScreenStackNativeComponent.d.ts.map +1 -1
  257. package/lib/typescript/fabric/stack/StackHeaderConfigIOSNativeComponent.d.ts +4 -0
  258. package/lib/typescript/fabric/stack/StackHeaderConfigIOSNativeComponent.d.ts.map +1 -1
  259. package/lib/typescript/fabric/stack/StackHeaderItemIOSNativeComponent.d.ts +6 -0
  260. package/lib/typescript/fabric/stack/StackHeaderItemIOSNativeComponent.d.ts.map +1 -1
  261. package/lib/typescript/fabric/tabs/TabsHostIOSNativeComponent.d.ts +3 -0
  262. package/lib/typescript/fabric/tabs/TabsHostIOSNativeComponent.d.ts.map +1 -1
  263. package/lib/typescript/fabric/tabs/TabsScreenAndroidNativeComponent.d.ts +6 -0
  264. package/lib/typescript/fabric/tabs/TabsScreenAndroidNativeComponent.d.ts.map +1 -1
  265. package/lib/typescript/fabric/tabs/TabsScreenIOSNativeComponent.d.ts +10 -1
  266. package/lib/typescript/fabric/tabs/TabsScreenIOSNativeComponent.d.ts.map +1 -1
  267. package/lib/typescript/legacy/components/ScreenStackItem.d.ts +25 -0
  268. package/lib/typescript/legacy/components/ScreenStackItem.d.ts.map +1 -1
  269. package/lib/typescript/legacy/components/helpers/prepareHeaderBarButtonItems.d.ts +4 -0
  270. package/lib/typescript/legacy/components/helpers/prepareHeaderBarButtonItems.d.ts.map +1 -1
  271. package/lib/typescript/legacy/types.d.ts +30 -0
  272. package/lib/typescript/legacy/types.d.ts.map +1 -1
  273. package/package.json +4 -2
  274. package/src/components/stack/header/StackHeaderConfig.android.tsx +6 -20
  275. package/src/components/stack/header/StackHeaderConfig.android.types.ts +4 -3
  276. package/src/components/stack/header/StackHeaderConfig.ios.tsx +10 -16
  277. package/src/components/stack/header/StackHeaderConfig.ios.types.ts +148 -2
  278. package/src/components/stack/header/StackHeaderConfig.tsx +12 -0
  279. package/src/components/stack/header/StackHeaderConfig.types.ts +2 -0
  280. package/src/components/stack/header/ios/StackHeaderItem.ios.tsx +9 -1
  281. package/src/components/stack/header/ios/StackHeaderItem.ios.types.ts +5 -0
  282. package/src/components/stack/host/StackHost.tsx +10 -3
  283. package/src/components/stack/host/StackHost.types.ts +25 -1
  284. package/src/components/stack/host/StackHost.web.tsx +1 -4
  285. package/src/components/stack/index.ts +9 -1
  286. package/src/components/tabs/host/TabsHost.ios.tsx +2 -0
  287. package/src/components/tabs/host/TabsHost.ios.types.ts +60 -0
  288. package/src/components/tabs/host/TabsHost.tsx +7 -0
  289. package/src/components/tabs/index.ts +9 -0
  290. package/src/components/tabs/screen/TabsScreen.android.tsx +8 -3
  291. package/src/components/tabs/screen/TabsScreen.android.types.ts +42 -7
  292. package/src/components/tabs/screen/TabsScreen.ios.tsx +22 -5
  293. package/src/components/tabs/screen/TabsScreen.ios.types.ts +79 -17
  294. package/src/components/tabs/screen/TabsScreen.tsx +7 -0
  295. package/src/components/tabs/screen/TabsScreenIcon.types.ts +117 -0
  296. package/src/components/tabs/screen/index.ts +1 -0
  297. package/src/fabric/legacy/ScreenStackHeaderSubviewNativeComponent.ts +6 -0
  298. package/src/fabric/legacy/ScreenStackNativeComponent.ts +2 -0
  299. package/src/fabric/stack/StackHeaderConfigIOSNativeComponent.ts +11 -0
  300. package/src/fabric/stack/StackHeaderItemIOSNativeComponent.ts +11 -0
  301. package/src/fabric/tabs/TabsHostIOSNativeComponent.ts +7 -0
  302. package/src/fabric/tabs/TabsScreenAndroidNativeComponent.ts +9 -0
  303. package/src/fabric/tabs/TabsScreenIOSNativeComponent.ts +20 -2
  304. package/src/legacy/components/ScreenStackItem.tsx +59 -12
  305. package/src/legacy/types.ts +31 -0
  306. package/android/src/main/java/com/swmansion/rnscreens/legacy/ScreenModalFragment.kt +0 -281
  307. package/android/src/main/java/com/swmansion/rnscreens/legacy/bottomsheet/BottomSheetDialogRootView.kt +0 -104
  308. package/android/src/main/java/com/swmansion/rnscreens/legacy/bottomsheet/BottomSheetDialogScreen.kt +0 -26
  309. package/lib/commonjs/components/stack/header/StackHeaderConfig.web.js +0 -10
  310. package/lib/commonjs/components/stack/header/StackHeaderConfig.web.js.map +0 -1
  311. package/lib/commonjs/components/tabs/host/TabsHost.d.js +0 -2
  312. package/lib/commonjs/components/tabs/host/TabsHost.d.js.map +0 -1
  313. package/lib/commonjs/components/tabs/host/TabsHost.web.js.map +0 -1
  314. package/lib/commonjs/components/tabs/screen/TabsScreen.d.js +0 -2
  315. package/lib/commonjs/components/tabs/screen/TabsScreen.d.js.map +0 -1
  316. package/lib/commonjs/components/tabs/screen/TabsScreen.web.js.map +0 -1
  317. package/lib/module/components/stack/header/StackHeaderConfig.d.js +0 -2
  318. package/lib/module/components/stack/header/StackHeaderConfig.web.js +0 -4
  319. package/lib/module/components/stack/header/StackHeaderConfig.web.js.map +0 -1
  320. package/lib/module/components/tabs/host/TabsHost.d.js +0 -2
  321. package/lib/module/components/tabs/host/TabsHost.d.js.map +0 -1
  322. package/lib/module/components/tabs/host/TabsHost.web.js +0 -4
  323. package/lib/module/components/tabs/host/TabsHost.web.js.map +0 -1
  324. package/lib/module/components/tabs/screen/TabsScreen.d.js +0 -2
  325. package/lib/module/components/tabs/screen/TabsScreen.d.js.map +0 -1
  326. package/lib/module/components/tabs/screen/TabsScreen.web.js +0 -4
  327. package/lib/module/components/tabs/screen/TabsScreen.web.js.map +0 -1
  328. package/lib/typescript/components/stack/header/StackHeaderConfig.web.d.ts +0 -7
  329. package/lib/typescript/components/stack/header/StackHeaderConfig.web.d.ts.map +0 -1
  330. package/lib/typescript/components/tabs/host/TabsHost.web.d.ts +0 -7
  331. package/lib/typescript/components/tabs/host/TabsHost.web.d.ts.map +0 -1
  332. package/lib/typescript/components/tabs/screen/TabsScreen.web.d.ts +0 -7
  333. package/lib/typescript/components/tabs/screen/TabsScreen.web.d.ts.map +0 -1
  334. package/src/components/stack/header/StackHeaderConfig.d.ts +0 -22
  335. package/src/components/stack/header/StackHeaderConfig.web.tsx +0 -5
  336. package/src/components/tabs/host/TabsHost.d.ts +0 -12
  337. package/src/components/tabs/screen/TabsScreen.d.ts +0 -12
@@ -59,6 +59,15 @@
59
59
  }];
60
60
  }
61
61
 
62
+ + (nullable UIImage *)symbolImageNamed:(nonnull NSString *)name
63
+ {
64
+ UIImage *image = [UIImage systemImageNamed:name];
65
+ if (image == nil) {
66
+ image = [UIImage imageNamed:name];
67
+ }
68
+ return image;
69
+ }
70
+
62
71
  + (nullable UIImage *)handleRenderingModeForImage:(nullable UIImage *)image isTemplate:(BOOL)isTemplate
63
72
  {
64
73
  if (isTemplate) {
@@ -89,6 +89,21 @@ static UIMenuOptions RNSMakeUIMenuOptionsFromConfig(NSDictionary *config);
89
89
  }
90
90
  #endif
91
91
 
92
+ // tvOS and visionOS are excluded on purpose: `visibilityPriority` exists there, but the only
93
+ // supported value is `standard`, which is already the default.
94
+ #if RNS_IPHONE_OS_VERSION_AVAILABLE(27_0) && !TARGET_OS_TV && !TARGET_OS_VISION
95
+ if (@available(iOS 27.0, *)) {
96
+ NSString *visibilityPriority = dict[@"visibilityPriority"];
97
+ if ([visibilityPriority isEqualToString:@"low"]) {
98
+ self.visibilityPriority = UIBarButtonItemVisibilityPriorityLow;
99
+ } else if ([visibilityPriority isEqualToString:@"high"]) {
100
+ self.visibilityPriority = UIBarButtonItemVisibilityPriorityHigh;
101
+ } else if ([visibilityPriority isEqualToString:@"standard"]) {
102
+ self.visibilityPriority = UIBarButtonItemVisibilityPriorityStandard;
103
+ }
104
+ }
105
+ #endif // RNS_IPHONE_OS_VERSION_AVAILABLE(27_0) && !TARGET_OS_TV && !TARGET_OS_VISION
106
+
92
107
  NSString *variant = dict[@"variant"];
93
108
  if (variant) {
94
109
  if ([variant isEqualToString:@"done"]) {
@@ -4,6 +4,7 @@
4
4
  #if defined(__cplusplus)
5
5
  #import <react/renderer/components/rnscreens/Props.h>
6
6
  #endif // __cplusplus
7
+ #import "RNSDefines.h"
7
8
  #import "RNSLegacyEnums.h"
8
9
 
9
10
  #if defined(__cplusplus)
@@ -31,6 +32,11 @@ namespace react = facebook::react;
31
32
  + (RNSScreenStackHeaderSubviewType)RNSScreenStackHeaderSubviewTypeFromCppEquivalent:
32
33
  (react::RNSScreenStackHeaderSubviewType)subviewType;
33
34
 
35
+ #if RNS_IPHONE_OS_VERSION_AVAILABLE(27_0)
36
+ + (UIBarButtonItemVisibilityPriority)UIBarButtonItemVisibilityPriorityFromCppEquivalent:
37
+ (react::RNSScreenStackHeaderSubviewVisibilityPriority)visibilityPriority API_AVAILABLE(ios(27.0));
38
+ #endif // RNS_IPHONE_OS_VERSION_AVAILABLE(27_0)
39
+
34
40
  + (RNSScreenReplaceAnimation)RNSScreenReplaceAnimationFromCppEquivalent:
35
41
  (react::RNSScreenReplaceAnimation)replaceAnimation;
36
42
 
@@ -118,6 +118,23 @@
118
118
  }
119
119
  }
120
120
 
121
+ #if RNS_IPHONE_OS_VERSION_AVAILABLE(27_0)
122
+ + (UIBarButtonItemVisibilityPriority)UIBarButtonItemVisibilityPriorityFromCppEquivalent:
123
+ (react::RNSScreenStackHeaderSubviewVisibilityPriority)visibilityPriority
124
+ {
125
+ switch (visibilityPriority) {
126
+ using enum react::RNSScreenStackHeaderSubviewVisibilityPriority;
127
+
128
+ case Low:
129
+ return UIBarButtonItemVisibilityPriorityLow;
130
+ case High:
131
+ return UIBarButtonItemVisibilityPriorityHigh;
132
+ case Standard:
133
+ return UIBarButtonItemVisibilityPriorityStandard;
134
+ }
135
+ }
136
+ #endif // RNS_IPHONE_OS_VERSION_AVAILABLE(27_0)
137
+
121
138
  + (RNSScreenReplaceAnimation)RNSScreenReplaceAnimationFromCppEquivalent:
122
139
  (react::RNSScreenReplaceAnimation)replaceAnimation
123
140
  {
@@ -0,0 +1,31 @@
1
+ #pragma once
2
+
3
+ #import <UIKit/UIKit.h>
4
+ #import "RNSDefines.h"
5
+
6
+ #if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0) && !TARGET_OS_TV
7
+
8
+ NS_ASSUME_NONNULL_BEGIN
9
+
10
+ @protocol RNSNavigationBarLayoutDelegate <NSObject>
11
+
12
+ - (void)navigationBarDidLayoutSubviews:(UINavigationBar *)navigationBar;
13
+
14
+ @end
15
+
16
+ /**
17
+ * Navigation bar used by the legacy stack (`RNSNavigationController`).
18
+ *
19
+ * It reports its layout passes to the `layoutDelegate`. On iOS 26+ UIKit can move the content of the navigation bar
20
+ * (e.g. on rotation or fold of iPhone Duo, or during the bar minimization on iOS 27) without any layout pass of the
21
+ * navigation controller's view, and layout of the bar is the only callback we receive in such a case.
22
+ */
23
+ @interface RNSNavigationBar : UINavigationBar
24
+
25
+ @property (nonatomic, weak, nullable) id<RNSNavigationBarLayoutDelegate> layoutDelegate;
26
+
27
+ @end
28
+
29
+ NS_ASSUME_NONNULL_END
30
+
31
+ #endif // RNS_IPHONE_OS_VERSION_AVAILABLE(26_0) && !TARGET_OS_TV
@@ -0,0 +1,15 @@
1
+ #import "RNSNavigationBar.h"
2
+
3
+ #if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0) && !TARGET_OS_TV
4
+
5
+ @implementation RNSNavigationBar
6
+
7
+ - (void)layoutSubviews
8
+ {
9
+ [super layoutSubviews];
10
+ [_layoutDelegate navigationBarDidLayoutSubviews:self];
11
+ }
12
+
13
+ @end
14
+
15
+ #endif // RNS_IPHONE_OS_VERSION_AVAILABLE(26_0) && !TARGET_OS_TV
@@ -53,6 +53,9 @@ struct ContentWrapperBox {
53
53
 
54
54
  @implementation RNSScreenView {
55
55
  __weak RCTScrollViewComponentView *_sheetsScrollView;
56
+ /// Tag of `_sheetsScrollView` at the time we started observing it. Fabric changes the tag when it recycles
57
+ /// the view, so a mismatch means the instance now belongs to some other component.
58
+ NSInteger _sheetsScrollViewTag;
56
59
 
57
60
  /// Up-to-date only when sheet is in `fitToContents` mode.
58
61
  CGFloat _sheetContentHeight;
@@ -99,6 +102,7 @@ struct ContentWrapperBox {
99
102
  _sheetExpandsWhenScrolledToEdge = YES;
100
103
  #endif // !TARGET_OS_TV
101
104
  _sheetsScrollView = nil;
105
+ _sheetsScrollViewTag = 0;
102
106
  _sheetContentHeight = 0.0;
103
107
  _markedForUnmountInCurrentTransaction = NO;
104
108
  _synchronousShadowStateUpdatesEnabled = YES;
@@ -134,16 +138,24 @@ RNS_IGNORE_SUPER_CALL_END
134
138
  if (_state != nullptr) {
135
139
  RNSScreenStackHeaderConfig *config = [self findHeaderConfig];
136
140
 
137
- // * in large title, ScrollView handles the offset of content so we cannot set it here also
138
- // * TODO: Why is it assumed in comment above, that large title uses scrollview here? What if only SafeAreaView is
141
+ // The content offset sent to the shadow tree must match the screen's native origin in the navigation controller's
142
+ // view. It is used by measurements & hit testing only, the size is delivered separately (`frameSize`).
143
+ // * On iOS 26+ (see `+[RNSScreenStackHeaderConfig screensExtendUnderOpaqueNavigationBar]`) the screen is always
144
+ // laid out under the navigation bar, so the origin is 0 and the `SafeAreaView` rendered by `ScreenStackItem`
145
+ // insets the content below an opaque bar. All branches below evaluate to 0 in that case.
146
+ // * Below iOS 26, when config.translucent == true, we use `edgesForExtendedLayout` and the screen is laid out
147
+ // under the navigation bar, therefore there is no need to set content offset in shadow tree.
148
+ // * Below iOS 26, in large title, ScrollView handles the offset of content so we cannot set it here also.
149
+ // TODO: Why is it assumed in comment above, that large title uses scrollview here? What if only SafeAreaView is
139
150
  // used?
140
- // * When config.translucent == true, we currently use `edgesForExtendedLayout` and the screen is laid out under the
141
- // navigation bar, therefore there is no need to set content offset in shadow tree.
142
151
  // * When this view is the modal root controller (presented in separate view hierarchy) it does not have navigation
143
152
  // bar! We send non-zero size to JS, for some reason. TODO: this needs to be investigated.
144
- const CGFloat effectiveContentOffsetY = config.largeTitle || config.translucent || self.isPresentedAsNativeModal
145
- ? 0
146
- : [_controller calculateHeaderHeightIsModal:self.isPresentedAsNativeModal];
153
+ // * Otherwise the offset must match the actual native origin of the screen. We can not derive it from the
154
+ // navigation bar geometry, because UIKit does not always lay out the screen right below the navigation bar
155
+ // (e.g. on iOS 26 `edgesForExtendedLayout` ignores the navigation bar's origin offset).
156
+ // See https://github.com/software-mansion/react-native-screens-labs/issues/1841
157
+ const CGFloat effectiveContentOffsetY =
158
+ config.largeTitle || config.translucent || self.isPresentedAsNativeModal ? 0 : [self originInNavigationView].y;
147
159
 
148
160
  auto newState = react::RNSScreenState{RCTSizeFromCGSize(self.bounds.size), {0, effectiveContentOffsetY}};
149
161
 
@@ -176,6 +188,17 @@ RNS_IGNORE_SUPER_CALL_END
176
188
  }
177
189
  }
178
190
 
191
+ // Returns origin of the screen in the coordinate space of its navigation controller's view, which matches
192
+ // the coordinate space of the screen stack in the shadow tree.
193
+ - (CGPoint)originInNavigationView
194
+ {
195
+ UIView *navigationView = _controller.navigationController.view;
196
+ if (navigationView == nil || ![self isDescendantOfView:navigationView]) {
197
+ return CGPointZero;
198
+ }
199
+ return [self convertPoint:CGPointZero toView:navigationView];
200
+ }
201
+
179
202
  - (void)applyFrameCorrectionForDescendantScrollView
180
203
  {
181
204
  // A dismissed sheet can still receive a layout pass after React has deleted it. By then Fabric
@@ -194,6 +217,8 @@ RNS_IGNORE_SUPER_CALL_END
194
217
  // we are going to overwrite it anyway.
195
218
  [scrollView addObserver:self forKeyPath:@"bounds" options:0 context:nil];
196
219
  }
220
+ // Refreshed even for the same instance: Fabric might have recycled it back into this sheet.
221
+ _sheetsScrollViewTag = scrollView != nil ? scrollView.tag : 0;
197
222
  if (scrollView != nil) {
198
223
  [self correctScrollViewFrame:scrollView withHeader:nil];
199
224
  }
@@ -222,6 +247,16 @@ RNS_IGNORE_SUPER_CALL_END
222
247
  return;
223
248
  }
224
249
 
250
+ // The sheet's content can remove its scroll view while the sheet stays presented. Fabric then recycles
251
+ // the instance (possibly into another screen), which changes its tag. Stop observing it instead of
252
+ // forcing it to this screen's frame. See #4785.
253
+ if (scrollView.tag != _sheetsScrollViewTag) {
254
+ [scrollView removeObserver:self forKeyPath:@"bounds" context:nil];
255
+ _sheetsScrollView = nil;
256
+ _sheetsScrollViewTag = 0;
257
+ return;
258
+ }
259
+
225
260
  RNSScreenContentWrapper *_Nullable contentWrapper = _contentWrapperBox.contentWrapper;
226
261
  if (contentWrapper != nil && [contentWrapper coerceChildScrollViewComponentSizeToSize:self.frame.size]) {
227
262
  return;
@@ -784,6 +819,7 @@ RNS_IGNORE_SUPER_CALL_END
784
819
  if (_sheetsScrollView != nil) {
785
820
  [_sheetsScrollView removeObserver:self forKeyPath:@"bounds" context:nil];
786
821
  _sheetsScrollView = nil;
822
+ _sheetsScrollViewTag = 0;
787
823
  }
788
824
 
789
825
  // We want to run after container updates are performed (transitions etc.)
@@ -1428,6 +1464,7 @@ Class<RCTComponentViewProtocol> RNSScreenCls(void)
1428
1464
  BOOL _isSwiping;
1429
1465
  BOOL _shouldNotify;
1430
1466
  BOOL _isRemovedFromParent;
1467
+ BOOL _hasNotifiedDismissed;
1431
1468
  }
1432
1469
 
1433
1470
  #pragma mark - Common
@@ -1439,6 +1476,7 @@ Class<RCTComponentViewProtocol> RNSScreenCls(void)
1439
1476
  _fakeView = [UIView new];
1440
1477
  _shouldNotify = YES;
1441
1478
  _isRemovedFromParent = NO;
1479
+ _hasNotifiedDismissed = NO;
1442
1480
  }
1443
1481
  return self;
1444
1482
  }
@@ -1535,7 +1573,7 @@ Class<RCTComponentViewProtocol> RNSScreenCls(void)
1535
1573
  [self.screenView notifyDismissCancelledWithDismissCount:_dismissCount];
1536
1574
  } else {
1537
1575
  // screen dismissed, send event
1538
- [self.screenView notifyDismissedWithCount:_dismissCount];
1576
+ [self notifyDismissedIfNeeded];
1539
1577
  }
1540
1578
  }
1541
1579
  // same flow as in viewDidAppear
@@ -1553,10 +1591,12 @@ Class<RCTComponentViewProtocol> RNSScreenCls(void)
1553
1591
  [super viewDidLayoutSubviews];
1554
1592
 
1555
1593
  // The below code makes the screen view adapt dimensions provided by the system. We take these
1556
- // into account only when the view is mounted under RNSNavigationController in which case system
1557
- // provides additional padding to account for possible header, and in the case when screen is
1558
- // shown as a native modal, as the final dimensions of the modal on iOS 12+ are shorter than the
1559
- // screen size
1594
+ // into account only when the view is mounted under RNSNavigationController, in which case the
1595
+ // navigation controller sizes the screen (below iOS 26 an opaque navigation bar is excluded from
1596
+ // the screen's frame, on iOS 26+ the screen always spans under the bar, see
1597
+ // `+[RNSScreenStackHeaderConfig screensExtendUnderOpaqueNavigationBar]`), and in the case when
1598
+ // screen is shown as a native modal, as the final dimensions of the modal on iOS 12+ are shorter
1599
+ // than the screen size.
1560
1600
  BOOL isDisplayedWithinUINavController = [self.parentViewController isKindOfClass:[RNSNavigationController class]];
1561
1601
  BOOL isTabScreen = [self.parentViewController isKindOfClass:RNSTabBarController.class];
1562
1602
 
@@ -1620,6 +1660,12 @@ Class<RCTComponentViewProtocol> RNSScreenCls(void)
1620
1660
  return 0;
1621
1661
  }
1622
1662
 
1663
+ #if RNS_IPHONE_OS_VERSION_AVAILABLE(27_0) && !TARGET_OS_TV
1664
+ if (@available(iOS 27.0, *)) {
1665
+ return [self calculateMinimizationAwareHeaderHeightInNavigationController:navctr];
1666
+ }
1667
+ #endif // RNS_IPHONE_OS_VERSION_AVAILABLE(27_0) && !TARGET_OS_TV
1668
+
1623
1669
  CGFloat navbarHeight = navctr.navigationBar.frame.size.height;
1624
1670
  #if !TARGET_OS_TV
1625
1671
  CGFloat navbarInset = navctr.navigationBar.frame.origin.y;
@@ -1631,6 +1677,35 @@ Class<RCTComponentViewProtocol> RNSScreenCls(void)
1631
1677
  return navbarHeight + navbarInset;
1632
1678
  }
1633
1679
 
1680
+ #if RNS_IPHONE_OS_VERSION_AVAILABLE(27_0) && !TARGET_OS_TV
1681
+ // On iOS 27 the navigation bar can minimize in response to scrolling (`UIBarMinimization`). The frame of
1682
+ // `UINavigationBar` does not follow the minimization, only its subviews (background, content view, large title,
1683
+ // search bar) do - they are shifted up within the bar. Therefore we measure the bottom edge of the subviews
1684
+ // instead, in the coordinate space of the navigation controller's view. The value is clamped to the bar's frame,
1685
+ // which is the maximum extent of the bar in the expanded state.
1686
+ - (CGFloat)calculateMinimizationAwareHeaderHeightInNavigationController:(UINavigationController *)navctr
1687
+ API_AVAILABLE(ios(27.0))
1688
+ {
1689
+ UINavigationBar *navbar = navctr.navigationBar;
1690
+ CGFloat navbarMaxY = CGRectGetMaxY([navbar convertRect:navbar.bounds toView:navctr.view]);
1691
+ CGFloat maxY = -CGFLOAT_MAX;
1692
+
1693
+ for (UIView *subview in navbar.subviews) {
1694
+ if (subview.isHidden || CGRectIsEmpty(subview.frame)) {
1695
+ continue;
1696
+ }
1697
+ maxY = MAX(maxY, CGRectGetMaxY([navbar convertRect:subview.frame toView:navctr.view]));
1698
+ }
1699
+
1700
+ if (maxY == -CGFLOAT_MAX) {
1701
+ // No measurable subviews, same as the pre-iOS 27 formula.
1702
+ return navbarMaxY;
1703
+ }
1704
+
1705
+ return MIN(maxY, navbarMaxY);
1706
+ }
1707
+ #endif // RNS_IPHONE_OS_VERSION_AVAILABLE(27_0) && !TARGET_OS_TV
1708
+
1634
1709
  - (void)calculateAndNotifyHeaderHeightChangeIsModal:(BOOL)isModal
1635
1710
  {
1636
1711
  CGFloat totalHeight = [self calculateHeaderHeightIsModal:isModal];
@@ -1698,6 +1773,34 @@ Class<RCTComponentViewProtocol> RNSScreenCls(void)
1698
1773
  _isRemovedFromParent = YES;
1699
1774
  }
1700
1775
 
1776
+ - (BOOL)isNativelyDismissedFromContainer
1777
+ {
1778
+ if (self.presentingViewController != nil) {
1779
+ return NO;
1780
+ }
1781
+ if (self.parentViewController == nil) {
1782
+ return YES;
1783
+ }
1784
+ if ([self.parentViewController isKindOfClass:UINavigationController.class]) {
1785
+ UINavigationController *navigationController = (UINavigationController *)self.parentViewController;
1786
+ return ![navigationController.viewControllers containsObject:self];
1787
+ }
1788
+ return NO;
1789
+ }
1790
+
1791
+ - (void)notifyDismissedIfNeeded
1792
+ {
1793
+ if (_hasNotifiedDismissed || self.screenView.preventNativeDismiss) {
1794
+ return;
1795
+ }
1796
+ if (![self isNativelyDismissedFromContainer]) {
1797
+ return;
1798
+ }
1799
+ _hasNotifiedDismissed = YES;
1800
+ _isRemovedFromParent = YES;
1801
+ [self.screenView notifyDismissedWithCount:_dismissCount];
1802
+ }
1803
+
1701
1804
  #pragma mark - transition progress related methods
1702
1805
 
1703
1806
  - (void)setupProgressNotification
@@ -1720,12 +1823,19 @@ Class<RCTComponentViewProtocol> RNSScreenCls(void)
1720
1823
  [self->_animationTimer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
1721
1824
  };
1722
1825
 
1826
+ BOOL notifyDismissWhenTransitionEnds = _closing;
1723
1827
  [self.transitionCoordinator
1724
1828
  animateAlongsideTransition:animation
1725
1829
  completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
1726
1830
  [self->_animationTimer setPaused:YES];
1727
1831
  [self->_animationTimer invalidate];
1728
1832
  [self->_fakeView removeFromSuperview];
1833
+ // iOS 27 can skip viewDidDisappear when a tab switch
1834
+ // interrupts popToRoot. If this screen was popped,
1835
+ // notify JS when the transition ends.
1836
+ if (notifyDismissWhenTransitionEnds) {
1837
+ [self notifyDismissedIfNeeded];
1838
+ }
1729
1839
  }];
1730
1840
  }
1731
1841
  }
@@ -53,6 +53,9 @@ namespace react = facebook::react;
53
53
  {
54
54
  UIViewController *controller = screenCtrl;
55
55
  float headerHeightErrata = 0.f;
56
+ // When screens extend under opaque navigation bars (iOS 26+), the `SafeAreaView` rendered inside this wrapper insets
57
+ // the content below the bar, so the bar height is already part of the wrapper's frame and must not be added again.
58
+ const BOOL wrapperFrameIncludesNavigationBar = [RNSScreenStackHeaderConfig screensExtendUnderOpaqueNavigationBar];
56
59
 
57
60
  do {
58
61
  if ([controller isKindOfClass:RNSScreen.class]) {
@@ -60,7 +63,7 @@ namespace react = facebook::react;
60
63
  if ([currentScreen.screenView registerContentWrapper:self contentHeightErrata:headerHeightErrata]) {
61
64
  break;
62
65
  }
63
- } else if ([controller isKindOfClass:RNSNavigationController.class]) {
66
+ } else if (!wrapperFrameIncludesNavigationBar && [controller isKindOfClass:RNSNavigationController.class]) {
64
67
  RNSNavigationController *navigationController = static_cast<RNSNavigationController *>(controller);
65
68
  UINavigationBar *navigationBar = navigationController.navigationBar;
66
69
 
@@ -12,6 +12,7 @@
12
12
  #import <react/renderer/components/rnscreens/RCTComponentViewHelpers.h>
13
13
  #import "RCTSurfaceTouchHandler+RNSUtility.h"
14
14
  #import "RNSDefines.h"
15
+ #import "RNSNavigationBar.h"
15
16
  #import "RNSPercentDrivenInteractiveTransition.h"
16
17
  #import "RNSScreen.h"
17
18
  #import "RNSScreenStackAnimator.h"
@@ -24,6 +25,10 @@
24
25
  #import "UIView+RNSUtility.h"
25
26
  #import "integrations/RNSDismissibleModalProtocol.h"
26
27
 
28
+ #if TARGET_OS_TV
29
+ #import <React/RCTTVRemoteHandler.h>
30
+ #endif // TARGET_OS_TV
31
+
27
32
  namespace react = facebook::react;
28
33
 
29
34
  @interface RNSScreenStackView () <UINavigationControllerDelegate,
@@ -36,9 +41,36 @@ namespace react = facebook::react;
36
41
  @property (nonatomic) BOOL updatingModals;
37
42
  @property (nonatomic) BOOL scheduleModalsUpdate;
38
43
 
44
+ #if TARGET_OS_TV
45
+ @property (nonatomic, assign) BOOL disableDefaultMenuAction;
46
+ @property (nonatomic, assign) BOOL observingTVMenuKeyNotifications;
47
+ #endif // TARGET_OS_TV
48
+
49
+ @end
50
+
51
+ #if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0) && !TARGET_OS_TV
52
+ @interface RNSNavigationController () <RNSNavigationBarLayoutDelegate>
39
53
  @end
54
+ #endif // RNS_IPHONE_OS_VERSION_AVAILABLE(26_0) && !TARGET_OS_TV
40
55
 
41
- @implementation RNSNavigationController
56
+ @implementation RNSNavigationController {
57
+ #if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0) && !TARGET_OS_TV
58
+ CGRect _lastNavigationBarFrame;
59
+ #endif // RNS_IPHONE_OS_VERSION_AVAILABLE(26_0) && !TARGET_OS_TV
60
+ }
61
+
62
+ - (instancetype)init
63
+ {
64
+ #if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0) && !TARGET_OS_TV
65
+ self = [super initWithNavigationBarClass:RNSNavigationBar.class toolbarClass:nil];
66
+ if (self != nil && [self.navigationBar isKindOfClass:RNSNavigationBar.class]) {
67
+ static_cast<RNSNavigationBar *>(self.navigationBar).layoutDelegate = self;
68
+ }
69
+ #else // RNS_IPHONE_OS_VERSION_AVAILABLE(26_0) && !TARGET_OS_TV
70
+ self = [super init];
71
+ #endif // RNS_IPHONE_OS_VERSION_AVAILABLE(26_0) && !TARGET_OS_TV
72
+ return self;
73
+ }
42
74
 
43
75
  #if !TARGET_OS_TV
44
76
  - (UIViewController *)childViewControllerForStatusBarStyle
@@ -59,25 +91,34 @@ namespace react = facebook::react;
59
91
  - (void)viewDidLayoutSubviews
60
92
  {
61
93
  [super viewDidLayoutSubviews];
94
+
95
+ #if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)
96
+ if (@available(iOS 26.0, *)) {
97
+ [self requestLayoutIfNavigationBarFrameChanged];
98
+ }
99
+ #endif // RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)
100
+
62
101
  if ([self.topViewController isKindOfClass:[RNSScreen class]]) {
63
102
  RNSScreen *screenController = (RNSScreen *)self.topViewController;
64
- BOOL isNotDismissingModal = screenController.presentedViewController == nil ||
65
- (screenController.presentedViewController != nil &&
66
- ![screenController.presentedViewController isBeingDismissed]);
67
- BOOL isPresentingSearchController =
68
- [screenController.presentedViewController isKindOfClass:UISearchController.class];
69
-
70
- // Calculate header height during simple transition from one screen to another.
71
- // If RNSScreen includes a navigation controller of type RNSNavigationController, it should not calculate
72
- // header height, as it could have nested stack.
73
- if (![screenController hasNestedStack] && (isPresentingSearchController || isNotDismissingModal)) {
74
- [screenController calculateAndNotifyHeaderHeightChangeIsModal:NO];
75
- }
76
-
103
+ [self notifyHeaderHeightChangeToScreen:screenController];
77
104
  [self maybeUpdateHeaderLayoutInfoInShadowTree:screenController];
78
105
  }
79
106
  }
80
107
 
108
+ - (void)notifyHeaderHeightChangeToScreen:(RNSScreen *)screenController
109
+ {
110
+ BOOL isNotDismissingModal = screenController.presentedViewController == nil ||
111
+ (screenController.presentedViewController != nil && ![screenController.presentedViewController isBeingDismissed]);
112
+ BOOL isPresentingSearchController = [screenController.presentedViewController isKindOfClass:UISearchController.class];
113
+
114
+ // Calculate header height during simple transition from one screen to another.
115
+ // If RNSScreen includes a navigation controller of type RNSNavigationController, it should not calculate
116
+ // header height, as it could have nested stack.
117
+ if (![screenController hasNestedStack] && (isPresentingSearchController || isNotDismissingModal)) {
118
+ [screenController calculateAndNotifyHeaderHeightChangeIsModal:NO];
119
+ }
120
+ }
121
+
81
122
  - (UIInterfaceOrientationMask)supportedInterfaceOrientations
82
123
  {
83
124
  return [self topViewController].supportedInterfaceOrientations;
@@ -116,7 +157,61 @@ namespace react = facebook::react;
116
157
 
117
158
  [headerConfig updateHeaderStateInShadowTreeInContextOfNavigationBar:self.navigationBar];
118
159
  }
119
- #endif
160
+
161
+ #if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)
162
+ - (void)requestLayoutIfNavigationBarFrameChanged API_AVAILABLE(ios(26.0))
163
+ {
164
+ // UIKit positions the navigation bar during layout of our view, but on iOS 26+ it does so after it has computed
165
+ // the safe area insets of the top view controller. The insets are therefore derived from the previous frame of
166
+ // the bar and they are not corrected until the next layout pass, which nobody requests.
167
+ // Since on iOS 26+ the screen is always laid out under the navigation bar, anything that relies on the insets
168
+ // (`SafeAreaView` rendered by `ScreenStackItem`, content inset adjustment of a scroll view) is misplaced by
169
+ // the distance the bar has travelled.
170
+ //
171
+ // When the frame of the bar has changed, we request another layout pass, in which UIKit computes the insets with
172
+ // the bar already in place. It runs in the same frame. The frame of the bar does not depend on the insets of
173
+ // the top view controller, so the bar is not moved in the additional pass and we do not request yet another one.
174
+ const CGRect navigationBarFrame = self.navigationBar.frame;
175
+ if (!CGRectEqualToRect(navigationBarFrame, _lastNavigationBarFrame)) {
176
+ _lastNavigationBarFrame = navigationBarFrame;
177
+ [self.view setNeedsLayout];
178
+ }
179
+ }
180
+
181
+ #pragma mark - RNSNavigationBarLayoutDelegate
182
+
183
+ - (void)navigationBarDidLayoutSubviews:(UINavigationBar *)navigationBar
184
+ {
185
+ // On iOS 26+ UIKit can position the content of the navigation bar after `viewDidLayoutSubviews`, in a layout pass
186
+ // that follows layout of the bar itself (e.g. title view on rotation of iPhone Duo), or without any layout pass of
187
+ // our view at all (bar minimization on iOS 27). None of our views receives a callback when it is moved this way,
188
+ // therefore we request layout of the header subviews. They are laid out later in the same layout pass, after their
189
+ // ancestors, and update their position in the shadow tree then.
190
+ // See https://github.com/software-mansion/react-native-screens-labs/issues/1841
191
+ if (@available(iOS 26.0, *)) {
192
+ if (![self.topViewController isKindOfClass:[RNSScreen class]]) {
193
+ return;
194
+ }
195
+
196
+ #if RNS_IPHONE_OS_VERSION_AVAILABLE(27_0)
197
+ // The bar minimization (iOS 27+) changes the visible height of the bar without changing its frame and without
198
+ // layout of our view, so this is the only place where we can follow it.
199
+ if (@available(iOS 27.0, *)) {
200
+ [self notifyHeaderHeightChangeToScreen:static_cast<RNSScreen *>(self.topViewController)];
201
+ }
202
+ #endif // RNS_IPHONE_OS_VERSION_AVAILABLE(27_0)
203
+
204
+ auto headerConfig = static_cast<RNSScreen *>(self.topViewController).screenView.findHeaderConfig;
205
+ if (headerConfig == nil || !headerConfig.shouldHeaderBeVisible) {
206
+ return;
207
+ }
208
+
209
+ [headerConfig setNeedsLayoutForHeaderSubviewsInNavigationBar:navigationBar];
210
+ }
211
+ }
212
+
213
+ #endif // RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)
214
+ #endif // !TARGET_OS_TV
120
215
 
121
216
  - (void)willMoveToParentViewController:(UIViewController *)parent
122
217
  {
@@ -283,8 +378,81 @@ RNS_IGNORE_SUPER_CALL_END
283
378
  [super didMoveToWindow];
284
379
  // for handling nested stacks
285
380
  [self maybeAddToParentAndUpdateContainer];
381
+ #if TARGET_OS_TV
382
+ if (self.window != nil) {
383
+ [self registerForTVMenuKeyNotificationsIfNeeded];
384
+ [self syncTVMenuKeyHandling];
385
+ } else {
386
+ [self unregisterForTVMenuKeyNotificationsIfNeeded];
387
+ }
388
+ #endif // TARGET_OS_TV
389
+ }
390
+
391
+ #if TARGET_OS_TV
392
+
393
+ - (void)registerForTVMenuKeyNotificationsIfNeeded
394
+ {
395
+ if (_observingTVMenuKeyNotifications) {
396
+ return;
397
+ }
398
+ _observingTVMenuKeyNotifications = YES;
399
+ NSNotificationCenter *center = NSNotificationCenter.defaultCenter;
400
+ [center addObserver:self
401
+ selector:@selector(handleTVMenuKeyNotification:)
402
+ name:RCTTVEnableMenuKeyNotification
403
+ object:nil];
404
+ [center addObserver:self
405
+ selector:@selector(handleTVMenuKeyNotification:)
406
+ name:RCTTVDisableMenuKeyNotification
407
+ object:nil];
408
+ }
409
+
410
+ - (void)unregisterForTVMenuKeyNotificationsIfNeeded
411
+ {
412
+ if (!_observingTVMenuKeyNotifications) {
413
+ return;
414
+ }
415
+ _observingTVMenuKeyNotifications = NO;
416
+ NSNotificationCenter *center = NSNotificationCenter.defaultCenter;
417
+ [center removeObserver:self name:RCTTVEnableMenuKeyNotification object:nil];
418
+ [center removeObserver:self name:RCTTVDisableMenuKeyNotification object:nil];
419
+ }
420
+
421
+ - (void)handleTVMenuKeyNotification:(NSNotification *)notification
422
+ {
423
+ [self syncTVMenuKeyHandling];
424
+ }
425
+
426
+ - (BOOL)shouldDisableUIKitMenuBackGesture
427
+ {
428
+ return _disableDefaultMenuAction && [RCTTVRemoteHandler useMenuKey];
429
+ }
430
+
431
+ - (nullable UITapGestureRecognizer *)uiKitMenuBackGestureRecognizer
432
+ {
433
+ for (UIGestureRecognizer *recognizer in _controller.view.gestureRecognizers) {
434
+ if (![recognizer isKindOfClass:UITapGestureRecognizer.class]) {
435
+ continue;
436
+ }
437
+ UITapGestureRecognizer *tapRecognizer = (UITapGestureRecognizer *)recognizer;
438
+ if ([tapRecognizer.allowedPressTypes containsObject:@(UIPressTypeMenu)]) {
439
+ return tapRecognizer;
440
+ }
441
+ }
442
+ return nil;
286
443
  }
287
444
 
445
+ - (void)syncTVMenuKeyHandling
446
+ {
447
+ UITapGestureRecognizer *menuBackGesture = [self uiKitMenuBackGestureRecognizer];
448
+ if (menuBackGesture == nil) {
449
+ return;
450
+ }
451
+ menuBackGesture.enabled = ![self shouldDisableUIKitMenuBackGesture];
452
+ }
453
+
454
+ #endif // TARGET_OS_TV
455
+
288
456
  - (void)maybeAddToParentAndUpdateContainer
289
457
  {
290
458
  BOOL wasScreenMounted = _controller.parentViewController != nil;
@@ -997,6 +1165,15 @@ RNS_IGNORE_SUPER_CALL_END
997
1165
  }
998
1166
  #endif
999
1167
 
1168
+ #if TARGET_OS_TV
1169
+ - (void)navigationController:(UINavigationController *)navigationController
1170
+ didShowViewController:(UIViewController *)viewController
1171
+ animated:(BOOL)animated
1172
+ {
1173
+ [self syncTVMenuKeyHandling];
1174
+ }
1175
+ #endif // TARGET_OS_TV
1176
+
1000
1177
  - (void)markChildUpdated
1001
1178
  {
1002
1179
  // In native stack this should be called only for `preload` purposes.
@@ -1279,6 +1456,13 @@ RNS_IGNORE_SUPER_CALL_END
1279
1456
  _controller.view.backgroundColor = _nativeContainerBackgroundColor;
1280
1457
  }
1281
1458
 
1459
+ #if TARGET_OS_TV
1460
+ if (newScreenProps.disableDefaultMenuAction != oldScreenProps.disableDefaultMenuAction) {
1461
+ _disableDefaultMenuAction = newScreenProps.disableDefaultMenuAction;
1462
+ [self syncTVMenuKeyHandling];
1463
+ }
1464
+ #endif // TARGET_OS_TV
1465
+
1282
1466
  [super updateProps:props oldProps:oldProps];
1283
1467
  }
1284
1468
 
@@ -1375,6 +1559,9 @@ RNS_IGNORE_SUPER_CALL_END
1375
1559
  - (void)prepareForRecycle
1376
1560
  {
1377
1561
  [super prepareForRecycle];
1562
+ #if TARGET_OS_TV
1563
+ [self unregisterForTVMenuKeyNotificationsIfNeeded];
1564
+ #endif // TARGET_OS_TV
1378
1565
  _reactSubviews = [NSMutableArray new];
1379
1566
 
1380
1567
  for (UIViewController *controller in _presentedModals) {