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.
- package/README.md +0 -4
- package/android/build.gradle +1 -0
- package/android/consumer-rules.pro +3 -0
- package/android/src/fabric/java/com/swmansion/rnscreens/legacy/FabricEnabledHeaderConfigViewGroup.kt +2 -0
- package/android/src/fabric/java/com/swmansion/rnscreens/legacy/FabricEnabledHeaderSubviewViewGroup.kt +2 -0
- package/android/src/fabric/java/com/swmansion/rnscreens/legacy/FabricEnabledViewGroup.kt +18 -3
- package/android/src/main/java/com/swmansion/rnscreens/RNScreensPackage.kt +2 -2
- package/android/src/main/java/com/swmansion/rnscreens/ScreensModule.kt +7 -3
- package/android/src/main/java/com/swmansion/rnscreens/common/colorscheme/ColorSchemeCoordinator.kt +11 -1
- package/android/src/main/java/com/swmansion/rnscreens/common/container/Container.kt +9 -0
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/coordinator/FormSheetAppearanceCoordinator.kt +2 -2
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/coordinator/FormSheetBehaviorController.kt +2 -2
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/coordinator/FormSheetDimensionsCoordinator.kt +6 -6
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/coordinator/FormSheetKeyboardCoordinator.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/coordinator/FormSheetNativeDismissCoordinator.kt +2 -2
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native/core → core/modals/formsheet/dialog}/FormSheetAvailableHeightProvider.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native/core → core/modals/formsheet/dialog}/FormSheetContainer.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native/core → core/modals/formsheet/dialog}/FormSheetDialog.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native/core → core/modals/formsheet/dialog}/FormSheetDialogManager.kt +26 -8
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/interfaces/FormSheetContentSizeChangeDelegate.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/interfaces/FormSheetContentSizeChangeProvider.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/interfaces/FormSheetDialogEventEmitter.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/model/FormSheetConfig.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/model/FormSheetDetents.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/presentation/FormSheetAnimatorFactory.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/presentation/FormSheetDimmingManager.kt +6 -18
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/presentation/FormSheetDismissalOrigin.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/presentation/FormSheetPresentation.kt +10 -10
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/presentation/FormSheetPresentationManager.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/presentation/FormSheetPresentationState.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/presentation/FormSheetStackRegistry.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/ext/FragmentManagerExt.kt +6 -0
- package/android/src/main/java/com/swmansion/rnscreens/helpers/FragmentManagerHelper.kt +0 -3
- package/android/src/main/java/com/swmansion/rnscreens/helpers/NoTintDrawable.kt +31 -0
- package/android/src/main/java/com/swmansion/rnscreens/helpers/ViewFinder.kt +0 -17
- package/android/src/main/java/com/swmansion/rnscreens/legacy/Screen.kt +29 -2
- package/android/src/main/java/com/swmansion/rnscreens/legacy/ScreenContainer.kt +35 -3
- package/android/src/main/java/com/swmansion/rnscreens/legacy/ScreenStack.kt +13 -0
- package/android/src/main/java/com/swmansion/rnscreens/legacy/ScreenStackHeaderConfig.kt +7 -1
- package/android/src/main/java/com/swmansion/rnscreens/legacy/ScreenStackHeaderSubviewManager.kt +7 -0
- package/android/src/main/java/com/swmansion/rnscreens/legacy/ScreenStackViewManager.kt +6 -0
- package/android/src/main/java/com/swmansion/rnscreens/legacy/bottomsheet/DimmingView.kt +2 -0
- package/android/src/main/java/com/swmansion/rnscreens/legacy/stack/views/ScreensCoordinatorLayout.kt +2 -0
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/contentwrapper/FormSheetContentWrapper.kt +3 -3
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/contentwrapper/FormSheetContentWrapperViewManager.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/event/FormSheetDetentChangedEvent.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/event/FormSheetDidAppearEvent.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/event/FormSheetDidDisappearEvent.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/event/FormSheetDismissEvent.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/event/FormSheetNativeDismissEvent.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/event/FormSheetNativeDismissPreventedEvent.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/event/FormSheetSyncFlushEvent.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/event/FormSheetWillAppearEvent.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/event/FormSheetWillDisappearEvent.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/host/FormSheetContentView.kt +3 -3
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/host/FormSheetHost.kt +5 -3
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/host/FormSheetHostEventEmitter.kt +11 -11
- package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/host/FormSheetHostViewManager.kt +10 -10
- package/android/src/main/java/com/swmansion/rnscreens/safearea/OnInterfaceInsetsChangeListener.kt +15 -0
- package/android/src/main/java/com/swmansion/rnscreens/safearea/SafeAreaProvider.kt +8 -8
- package/android/src/main/java/com/swmansion/rnscreens/safearea/SafeAreaView.kt +3 -2
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/StackHeaderApplicator.kt +0 -52
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/StackHeaderCoordinatorLayout.kt +112 -59
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/appbar/StackHeaderAppBarLayout.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/appbar/StackHeaderToolbar.kt +74 -0
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderConfig.kt +4 -73
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderConfigurationObserver.kt +5 -2
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderConfigurationProviding.kt +0 -6
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderInvalidationFlags.kt +3 -7
- package/android/src/main/java/com/swmansion/rnscreens/stack/host/FragmentOperation.kt +1 -0
- package/android/src/main/java/com/swmansion/rnscreens/stack/host/FragmentOperationExecutor.kt +2 -1
- package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackContainer.kt +57 -19
- package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackContainerDelegate.kt +7 -0
- package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackContainerUpdateCoordinator.kt +5 -3
- package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackHost.kt +14 -5
- package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackUpdateBatchStateProviding.kt +10 -0
- package/android/src/main/java/com/swmansion/rnscreens/stack/screen/PreventNativeDismissCallback.kt +2 -2
- package/android/src/main/java/com/swmansion/rnscreens/stack/screen/StackScreenFragment.kt +33 -12
- package/android/src/main/java/com/swmansion/rnscreens/tabs/appearance/TabsAppearanceApplicator.kt +106 -10
- package/android/src/main/java/com/swmansion/rnscreens/tabs/appearance/TabsAppearanceCoordinator.kt +44 -3
- package/android/src/main/java/com/swmansion/rnscreens/tabs/appearance/TabsAppearanceModel.kt +3 -0
- package/android/src/main/java/com/swmansion/rnscreens/tabs/container/CustomBottomNavigationView.kt +21 -1
- package/android/src/main/java/com/swmansion/rnscreens/tabs/container/TabsContainer.kt +28 -13
- package/android/src/main/java/com/swmansion/rnscreens/tabs/container/TabsContainerDelegate.kt +11 -0
- package/android/src/main/java/com/swmansion/rnscreens/tabs/host/TabsHost.kt +12 -2
- package/android/src/main/java/com/swmansion/rnscreens/tabs/screen/TabsScreen.kt +41 -17
- package/android/src/main/java/com/swmansion/rnscreens/tabs/screen/TabsScreenDelegate.kt +6 -0
- package/android/src/main/java/com/swmansion/rnscreens/tabs/screen/TabsScreenIcon.kt +82 -0
- package/android/src/main/java/com/swmansion/rnscreens/tabs/screen/TabsScreenIconTinting.kt +17 -0
- package/android/src/main/java/com/swmansion/rnscreens/tabs/screen/TabsScreenViewManager.kt +26 -19
- package/android/src/main/java/com/swmansion/rnscreens/utils/DimensionUtils.kt +14 -7
- package/android/src/main/java/com/swmansion/rnscreens/utils/RNSLog.kt +22 -6
- package/ios/RNSEnums.h +14 -1
- package/ios/helpers/image/RNSImageLoadingHelper.h +7 -0
- package/ios/helpers/image/RNSImageLoadingHelper.mm +9 -0
- package/ios/legacy/RNSBarButtonItem.mm +15 -0
- package/ios/legacy/RNSConvert.h +6 -0
- package/ios/legacy/RNSConvert.mm +17 -0
- package/ios/legacy/RNSNavigationBar.h +31 -0
- package/ios/legacy/RNSNavigationBar.mm +15 -0
- package/ios/legacy/RNSScreen.mm +122 -12
- package/ios/legacy/RNSScreenContentWrapper.mm +4 -1
- package/ios/legacy/RNSScreenStack.mm +202 -15
- package/ios/legacy/RNSScreenStackHeaderConfig.h +30 -0
- package/ios/legacy/RNSScreenStackHeaderConfig.mm +70 -17
- package/ios/legacy/RNSScreenStackHeaderSubview.mm +33 -0
- package/ios/safe-area/RNSSafeAreaViewComponentView.mm +1 -12
- package/ios/stack/conversion/RNSConversions-Stack.h +30 -0
- package/ios/stack/conversion/RNSConversions-Stack.mm +79 -0
- package/ios/stack/header/RNSHeaderItemVisibilityPriority.h +9 -0
- package/ios/stack/header/RNSStackHeaderConfigComponentView.h +4 -0
- package/ios/stack/header/RNSStackHeaderConfigComponentView.mm +40 -6
- package/ios/stack/header/RNSStackHeaderConfigDataProviding.h +5 -0
- package/ios/stack/header/RNSStackHeaderContentFactory.mm +3 -0
- package/ios/stack/header/RNSStackHeaderItemComponentView.h +9 -2
- package/ios/stack/header/RNSStackHeaderItemComponentView.mm +57 -3
- package/ios/stack/header/RNSStackHeaderItemDataProviding.h +5 -0
- package/ios/stack/header/RNSStackHeaderMenuCoordinator.h +13 -0
- package/ios/stack/header/RNSStackHeaderMenuCoordinator.mm +24 -0
- package/ios/stack/header/RNSStackHeaderMenuFinder.h +8 -1
- package/ios/stack/header/RNSStackHeaderMenuFinder.mm +26 -9
- package/ios/stack/screen/RNSStackScreenComponentView.h +2 -1
- package/ios/stack/screen/RNSStackScreenComponentView.mm +23 -0
- package/ios/stack/screen/RNSStackScreenHeaderCoordinator.mm +81 -8
- package/ios/tabs/RNSTabBarAppearanceCoordinator.mm +79 -16
- package/ios/tabs/RNSTabBarItemCoordinator.h +16 -0
- package/ios/tabs/RNSTabBarItemCoordinator.mm +143 -0
- package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryWrapperView.h +24 -0
- package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryWrapperView.mm +22 -0
- package/ios/tabs/conversion/RNSConversions-Tabs.h +22 -0
- package/ios/tabs/conversion/RNSConversions-Tabs.mm +97 -3
- package/ios/tabs/host/RNSTabBarController.h +16 -0
- package/ios/tabs/host/RNSTabBarController.mm +516 -107
- package/ios/tabs/host/RNSTabsHostComponentView.h +2 -0
- package/ios/tabs/host/RNSTabsHostComponentView.mm +32 -2
- package/ios/tabs/screen/RNSTabsScreenComponentView.h +21 -0
- package/ios/tabs/screen/RNSTabsScreenComponentView.mm +54 -69
- package/ios/tabs/screen/RNSTabsScreenViewController.mm +1 -0
- package/ios/utils/RNSDefines.h +12 -7
- package/lib/commonjs/components/stack/header/StackHeaderConfig.android.js +3 -2
- package/lib/commonjs/components/stack/header/StackHeaderConfig.android.js.map +1 -1
- package/lib/commonjs/components/stack/header/StackHeaderConfig.ios.js +8 -3
- package/lib/commonjs/components/stack/header/StackHeaderConfig.ios.js.map +1 -1
- package/lib/commonjs/components/stack/header/StackHeaderConfig.js +17 -0
- package/lib/commonjs/components/stack/header/StackHeaderConfig.js.map +1 -0
- package/lib/commonjs/components/stack/header/ios/StackHeaderItem.ios.js +4 -1
- package/lib/commonjs/components/stack/header/ios/StackHeaderItem.ios.js.map +1 -1
- package/lib/commonjs/components/stack/host/StackHost.js +8 -1
- package/lib/commonjs/components/stack/host/StackHost.js.map +1 -1
- package/lib/commonjs/components/stack/host/StackHost.web.js +0 -2
- package/lib/commonjs/components/stack/host/StackHost.web.js.map +1 -1
- package/lib/commonjs/components/stack/index.js.map +1 -1
- package/lib/commonjs/components/tabs/host/TabsHost.ios.js +2 -0
- package/lib/commonjs/components/tabs/host/TabsHost.ios.js.map +1 -1
- package/lib/commonjs/components/tabs/host/{TabsHost.web.js → TabsHost.js} +1 -1
- package/lib/commonjs/components/tabs/host/TabsHost.js.map +1 -0
- package/lib/commonjs/components/tabs/index.js.map +1 -1
- package/lib/commonjs/components/tabs/screen/TabsScreen.android.js +3 -1
- package/lib/commonjs/components/tabs/screen/TabsScreen.android.js.map +1 -1
- package/lib/commonjs/components/tabs/screen/TabsScreen.ios.js +10 -1
- package/lib/commonjs/components/tabs/screen/TabsScreen.ios.js.map +1 -1
- package/lib/commonjs/components/tabs/screen/{TabsScreen.web.js → TabsScreen.js} +1 -1
- package/lib/commonjs/components/tabs/screen/TabsScreen.js.map +1 -0
- package/lib/commonjs/components/{stack/header/StackHeaderConfig.d.js → tabs/screen/TabsScreenIcon.types.js} +1 -1
- package/lib/commonjs/components/{stack/header/StackHeaderConfig.d.js.map → tabs/screen/TabsScreenIcon.types.js.map} +1 -1
- package/lib/commonjs/fabric/legacy/ScreenStackHeaderSubviewNativeComponent.js.map +1 -1
- package/lib/commonjs/fabric/legacy/ScreenStackNativeComponent.js.map +1 -1
- package/lib/commonjs/fabric/stack/StackHeaderConfigIOSNativeComponent.js.map +1 -1
- package/lib/commonjs/fabric/stack/StackHeaderItemIOSNativeComponent.js.map +1 -1
- package/lib/commonjs/fabric/tabs/TabsHostIOSNativeComponent.js.map +1 -1
- package/lib/commonjs/fabric/tabs/TabsScreenAndroidNativeComponent.js.map +1 -1
- package/lib/commonjs/fabric/tabs/TabsScreenIOSNativeComponent.js +1 -1
- package/lib/commonjs/fabric/tabs/TabsScreenIOSNativeComponent.js.map +1 -1
- package/lib/commonjs/legacy/components/ScreenStackItem.js +31 -11
- package/lib/commonjs/legacy/components/ScreenStackItem.js.map +1 -1
- package/lib/module/components/stack/header/StackHeaderConfig.android.js +4 -3
- package/lib/module/components/stack/header/StackHeaderConfig.android.js.map +1 -1
- package/lib/module/components/stack/header/StackHeaderConfig.ios.js +9 -4
- package/lib/module/components/stack/header/StackHeaderConfig.ios.js.map +1 -1
- package/lib/module/components/stack/header/StackHeaderConfig.js +10 -0
- package/lib/module/components/stack/header/StackHeaderConfig.js.map +1 -0
- package/lib/module/components/stack/header/ios/StackHeaderItem.ios.js +4 -1
- package/lib/module/components/stack/header/ios/StackHeaderItem.ios.js.map +1 -1
- package/lib/module/components/stack/host/StackHost.js +9 -2
- package/lib/module/components/stack/host/StackHost.js.map +1 -1
- package/lib/module/components/stack/host/StackHost.web.js +0 -3
- package/lib/module/components/stack/host/StackHost.web.js.map +1 -1
- package/lib/module/components/stack/index.js.map +1 -1
- package/lib/module/components/tabs/host/TabsHost.ios.js +2 -0
- package/lib/module/components/tabs/host/TabsHost.ios.js.map +1 -1
- package/{src/components/tabs/host/TabsHost.web.tsx → lib/module/components/tabs/host/TabsHost.js} +1 -2
- package/lib/module/components/tabs/host/TabsHost.js.map +1 -0
- package/lib/module/components/tabs/index.js.map +1 -1
- package/lib/module/components/tabs/screen/TabsScreen.android.js +3 -1
- package/lib/module/components/tabs/screen/TabsScreen.android.js.map +1 -1
- package/lib/module/components/tabs/screen/TabsScreen.ios.js +10 -1
- package/lib/module/components/tabs/screen/TabsScreen.ios.js.map +1 -1
- package/{src/components/tabs/screen/TabsScreen.web.tsx → lib/module/components/tabs/screen/TabsScreen.js} +1 -2
- package/lib/module/components/tabs/screen/TabsScreen.js.map +1 -0
- package/lib/module/components/tabs/screen/TabsScreenIcon.types.js +2 -0
- package/lib/module/components/{stack/header/StackHeaderConfig.d.js.map → tabs/screen/TabsScreenIcon.types.js.map} +1 -1
- package/lib/module/fabric/legacy/ScreenStackHeaderSubviewNativeComponent.js.map +1 -1
- package/lib/module/fabric/legacy/ScreenStackNativeComponent.js.map +1 -1
- package/lib/module/fabric/stack/StackHeaderConfigIOSNativeComponent.js.map +1 -1
- package/lib/module/fabric/stack/StackHeaderItemIOSNativeComponent.js.map +1 -1
- package/lib/module/fabric/tabs/TabsHostIOSNativeComponent.js.map +1 -1
- package/lib/module/fabric/tabs/TabsScreenAndroidNativeComponent.js.map +1 -1
- package/lib/module/fabric/tabs/TabsScreenIOSNativeComponent.js +1 -1
- package/lib/module/fabric/tabs/TabsScreenIOSNativeComponent.js.map +1 -1
- package/lib/module/legacy/components/ScreenStackItem.js +31 -11
- package/lib/module/legacy/components/ScreenStackItem.js.map +1 -1
- package/lib/typescript/components/stack/header/StackHeaderConfig.android.d.ts +6 -3
- package/lib/typescript/components/stack/header/StackHeaderConfig.android.d.ts.map +1 -1
- package/lib/typescript/components/stack/header/StackHeaderConfig.android.types.d.ts +4 -3
- package/lib/typescript/components/stack/header/StackHeaderConfig.android.types.d.ts.map +1 -1
- package/lib/typescript/components/stack/header/StackHeaderConfig.d.ts +8 -0
- package/lib/typescript/components/stack/header/StackHeaderConfig.d.ts.map +1 -0
- package/lib/typescript/components/stack/header/StackHeaderConfig.ios.d.ts +6 -3
- package/lib/typescript/components/stack/header/StackHeaderConfig.ios.d.ts.map +1 -1
- package/lib/typescript/components/stack/header/StackHeaderConfig.ios.types.d.ts +137 -2
- package/lib/typescript/components/stack/header/StackHeaderConfig.ios.types.d.ts.map +1 -1
- package/lib/typescript/components/stack/header/StackHeaderConfig.types.d.ts +2 -0
- package/lib/typescript/components/stack/header/StackHeaderConfig.types.d.ts.map +1 -1
- package/lib/typescript/components/stack/header/ios/StackHeaderItem.ios.d.ts.map +1 -1
- package/lib/typescript/components/stack/header/ios/StackHeaderItem.ios.types.d.ts +5 -0
- package/lib/typescript/components/stack/header/ios/StackHeaderItem.ios.types.d.ts.map +1 -1
- package/lib/typescript/components/stack/host/StackHost.d.ts.map +1 -1
- package/lib/typescript/components/stack/host/StackHost.types.d.ts +24 -1
- package/lib/typescript/components/stack/host/StackHost.types.d.ts.map +1 -1
- package/lib/typescript/components/stack/host/StackHost.web.d.ts +1 -3
- package/lib/typescript/components/stack/host/StackHost.web.d.ts.map +1 -1
- package/lib/typescript/components/stack/index.d.ts +4 -3
- package/lib/typescript/components/stack/index.d.ts.map +1 -1
- package/lib/typescript/components/tabs/host/TabsHost.d.ts +5 -0
- package/lib/typescript/components/tabs/host/TabsHost.d.ts.map +1 -0
- package/lib/typescript/components/tabs/host/TabsHost.ios.d.ts.map +1 -1
- package/lib/typescript/components/tabs/host/TabsHost.ios.types.d.ts +56 -0
- package/lib/typescript/components/tabs/host/TabsHost.ios.types.d.ts.map +1 -1
- package/lib/typescript/components/tabs/index.d.ts +4 -6
- package/lib/typescript/components/tabs/index.d.ts.map +1 -1
- package/lib/typescript/components/tabs/screen/TabsScreen.android.d.ts.map +1 -1
- package/lib/typescript/components/tabs/screen/TabsScreen.android.types.d.ts +42 -7
- package/lib/typescript/components/tabs/screen/TabsScreen.android.types.d.ts.map +1 -1
- package/lib/typescript/components/tabs/screen/TabsScreen.d.ts +5 -0
- package/lib/typescript/components/tabs/screen/TabsScreen.d.ts.map +1 -0
- package/lib/typescript/components/tabs/screen/TabsScreen.ios.d.ts.map +1 -1
- package/lib/typescript/components/tabs/screen/TabsScreen.ios.types.d.ts +78 -13
- package/lib/typescript/components/tabs/screen/TabsScreen.ios.types.d.ts.map +1 -1
- package/lib/typescript/components/tabs/screen/TabsScreenIcon.types.d.ts +91 -0
- package/lib/typescript/components/tabs/screen/TabsScreenIcon.types.d.ts.map +1 -0
- package/lib/typescript/components/tabs/screen/index.d.ts +1 -0
- package/lib/typescript/components/tabs/screen/index.d.ts.map +1 -1
- package/lib/typescript/fabric/legacy/ScreenStackHeaderSubviewNativeComponent.d.ts +2 -0
- package/lib/typescript/fabric/legacy/ScreenStackHeaderSubviewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/fabric/legacy/ScreenStackNativeComponent.d.ts +1 -0
- package/lib/typescript/fabric/legacy/ScreenStackNativeComponent.d.ts.map +1 -1
- package/lib/typescript/fabric/stack/StackHeaderConfigIOSNativeComponent.d.ts +4 -0
- package/lib/typescript/fabric/stack/StackHeaderConfigIOSNativeComponent.d.ts.map +1 -1
- package/lib/typescript/fabric/stack/StackHeaderItemIOSNativeComponent.d.ts +6 -0
- package/lib/typescript/fabric/stack/StackHeaderItemIOSNativeComponent.d.ts.map +1 -1
- package/lib/typescript/fabric/tabs/TabsHostIOSNativeComponent.d.ts +3 -0
- package/lib/typescript/fabric/tabs/TabsHostIOSNativeComponent.d.ts.map +1 -1
- package/lib/typescript/fabric/tabs/TabsScreenAndroidNativeComponent.d.ts +6 -0
- package/lib/typescript/fabric/tabs/TabsScreenAndroidNativeComponent.d.ts.map +1 -1
- package/lib/typescript/fabric/tabs/TabsScreenIOSNativeComponent.d.ts +10 -1
- package/lib/typescript/fabric/tabs/TabsScreenIOSNativeComponent.d.ts.map +1 -1
- package/lib/typescript/legacy/components/ScreenStackItem.d.ts +25 -0
- package/lib/typescript/legacy/components/ScreenStackItem.d.ts.map +1 -1
- package/lib/typescript/legacy/components/helpers/prepareHeaderBarButtonItems.d.ts +4 -0
- package/lib/typescript/legacy/components/helpers/prepareHeaderBarButtonItems.d.ts.map +1 -1
- package/lib/typescript/legacy/types.d.ts +30 -0
- package/lib/typescript/legacy/types.d.ts.map +1 -1
- package/package.json +4 -2
- package/src/components/stack/header/StackHeaderConfig.android.tsx +6 -20
- package/src/components/stack/header/StackHeaderConfig.android.types.ts +4 -3
- package/src/components/stack/header/StackHeaderConfig.ios.tsx +10 -16
- package/src/components/stack/header/StackHeaderConfig.ios.types.ts +148 -2
- package/src/components/stack/header/StackHeaderConfig.tsx +12 -0
- package/src/components/stack/header/StackHeaderConfig.types.ts +2 -0
- package/src/components/stack/header/ios/StackHeaderItem.ios.tsx +9 -1
- package/src/components/stack/header/ios/StackHeaderItem.ios.types.ts +5 -0
- package/src/components/stack/host/StackHost.tsx +10 -3
- package/src/components/stack/host/StackHost.types.ts +25 -1
- package/src/components/stack/host/StackHost.web.tsx +1 -4
- package/src/components/stack/index.ts +9 -1
- package/src/components/tabs/host/TabsHost.ios.tsx +2 -0
- package/src/components/tabs/host/TabsHost.ios.types.ts +60 -0
- package/src/components/tabs/host/TabsHost.tsx +7 -0
- package/src/components/tabs/index.ts +9 -0
- package/src/components/tabs/screen/TabsScreen.android.tsx +8 -3
- package/src/components/tabs/screen/TabsScreen.android.types.ts +42 -7
- package/src/components/tabs/screen/TabsScreen.ios.tsx +22 -5
- package/src/components/tabs/screen/TabsScreen.ios.types.ts +79 -17
- package/src/components/tabs/screen/TabsScreen.tsx +7 -0
- package/src/components/tabs/screen/TabsScreenIcon.types.ts +117 -0
- package/src/components/tabs/screen/index.ts +1 -0
- package/src/fabric/legacy/ScreenStackHeaderSubviewNativeComponent.ts +6 -0
- package/src/fabric/legacy/ScreenStackNativeComponent.ts +2 -0
- package/src/fabric/stack/StackHeaderConfigIOSNativeComponent.ts +11 -0
- package/src/fabric/stack/StackHeaderItemIOSNativeComponent.ts +11 -0
- package/src/fabric/tabs/TabsHostIOSNativeComponent.ts +7 -0
- package/src/fabric/tabs/TabsScreenAndroidNativeComponent.ts +9 -0
- package/src/fabric/tabs/TabsScreenIOSNativeComponent.ts +20 -2
- package/src/legacy/components/ScreenStackItem.tsx +59 -12
- package/src/legacy/types.ts +31 -0
- package/android/src/main/java/com/swmansion/rnscreens/legacy/ScreenModalFragment.kt +0 -281
- package/android/src/main/java/com/swmansion/rnscreens/legacy/bottomsheet/BottomSheetDialogRootView.kt +0 -104
- package/android/src/main/java/com/swmansion/rnscreens/legacy/bottomsheet/BottomSheetDialogScreen.kt +0 -26
- package/lib/commonjs/components/stack/header/StackHeaderConfig.web.js +0 -10
- package/lib/commonjs/components/stack/header/StackHeaderConfig.web.js.map +0 -1
- package/lib/commonjs/components/tabs/host/TabsHost.d.js +0 -2
- package/lib/commonjs/components/tabs/host/TabsHost.d.js.map +0 -1
- package/lib/commonjs/components/tabs/host/TabsHost.web.js.map +0 -1
- package/lib/commonjs/components/tabs/screen/TabsScreen.d.js +0 -2
- package/lib/commonjs/components/tabs/screen/TabsScreen.d.js.map +0 -1
- package/lib/commonjs/components/tabs/screen/TabsScreen.web.js.map +0 -1
- package/lib/module/components/stack/header/StackHeaderConfig.d.js +0 -2
- package/lib/module/components/stack/header/StackHeaderConfig.web.js +0 -4
- package/lib/module/components/stack/header/StackHeaderConfig.web.js.map +0 -1
- package/lib/module/components/tabs/host/TabsHost.d.js +0 -2
- package/lib/module/components/tabs/host/TabsHost.d.js.map +0 -1
- package/lib/module/components/tabs/host/TabsHost.web.js +0 -4
- package/lib/module/components/tabs/host/TabsHost.web.js.map +0 -1
- package/lib/module/components/tabs/screen/TabsScreen.d.js +0 -2
- package/lib/module/components/tabs/screen/TabsScreen.d.js.map +0 -1
- package/lib/module/components/tabs/screen/TabsScreen.web.js +0 -4
- package/lib/module/components/tabs/screen/TabsScreen.web.js.map +0 -1
- package/lib/typescript/components/stack/header/StackHeaderConfig.web.d.ts +0 -7
- package/lib/typescript/components/stack/header/StackHeaderConfig.web.d.ts.map +0 -1
- package/lib/typescript/components/tabs/host/TabsHost.web.d.ts +0 -7
- package/lib/typescript/components/tabs/host/TabsHost.web.d.ts.map +0 -1
- package/lib/typescript/components/tabs/screen/TabsScreen.web.d.ts +0 -7
- package/lib/typescript/components/tabs/screen/TabsScreen.web.d.ts.map +0 -1
- package/src/components/stack/header/StackHeaderConfig.d.ts +0 -22
- package/src/components/stack/header/StackHeaderConfig.web.tsx +0 -5
- package/src/components/tabs/host/TabsHost.d.ts +0 -12
- package/src/components/tabs/screen/TabsScreen.d.ts +0 -12
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
#import <objc/runtime.h>
|
|
6
6
|
#import <limits>
|
|
7
7
|
#import "NSString+RNSUtility.h"
|
|
8
|
+
#import "RNSDefines.h"
|
|
8
9
|
#import "RNSLog.h"
|
|
9
10
|
#import "RNSParentContainerItemRegistry.h"
|
|
10
11
|
#import "RNSScreenWindowTraits.h"
|
|
@@ -70,6 +71,11 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
70
71
|
@implementation RNSTabBarController {
|
|
71
72
|
NSArray<RNSTabsScreenViewController *> *_Nullable _tabScreenControllers;
|
|
72
73
|
|
|
74
|
+
/// Controllers currently installed in UIKit (see `installScreenControllers:animated:`).
|
|
75
|
+
/// On the UITab path `UITabBarController.viewControllers` is empty once `tabs` is set,
|
|
76
|
+
/// so the installed set is tracked here; on the legacy path UIKit itself is the source of truth.
|
|
77
|
+
NSArray<RNSTabsScreenViewController *> *_Nullable _installedScreenControllers;
|
|
78
|
+
|
|
73
79
|
/// This property is nullable until first container update. Later it MUST NOT be nil.
|
|
74
80
|
RNSTabsNavigationState *_Nullable _navigationState;
|
|
75
81
|
|
|
@@ -86,20 +92,46 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
86
92
|
/// delegate handling). Setter overrides skip reconciliation while this flag is set.
|
|
87
93
|
BOOL _isHandlingExplicitSelectionUpdate;
|
|
88
94
|
|
|
95
|
+
/// UITab path only. YES while this controller itself writes `tabs` / `selectedTab` /
|
|
96
|
+
/// `selectedViewController`. UIKit sends `didSelectTab:` synchronously from inside those writes -
|
|
97
|
+
/// this flag filters them out, so that only UIKit-driven (user) selections reach the delegate
|
|
98
|
+
/// logic. Marking user selections instead is not viable: for `UISearchTab`, a mount transaction
|
|
99
|
+
/// can land between `shouldSelectTab:` and `didSelectTab:`.
|
|
100
|
+
BOOL _isApplyingProgrammaticTabSelection;
|
|
101
|
+
|
|
102
|
+
/// UITab path only. Set in `tabBar:didSelectItem:` when the user selects the More tab while
|
|
103
|
+
/// another tab is active. Tapping More does not necessarily show the More list - the stack may
|
|
104
|
+
/// re-display a hosted screen retained from a previous visit - so the decision between emitting
|
|
105
|
+
/// `onMoreTabSelected` and progressing state to the re-displayed screen is deferred to
|
|
106
|
+
/// `willShowViewController:`, which reports what actually shows.
|
|
107
|
+
BOOL _pendingMoreTabSelectedEmit;
|
|
108
|
+
|
|
89
109
|
RNSTabsNavigationStateObserverRegistry *_observerRegistry;
|
|
90
110
|
|
|
91
111
|
RNSParentContainerItemRegistry *_Nonnull _parentContainerRegistry;
|
|
112
|
+
|
|
113
|
+
/// Screen key of the prominent tab (iOS 27+); nil when not set, letting UIKit decide. Kept as the screen key,
|
|
114
|
+
/// because `UISearchTab` gets a system-assigned identifier & the tabs are rebuilt on children updates.
|
|
115
|
+
NSString *_Nullable _prominentScreenKey;
|
|
116
|
+
|
|
117
|
+
BOOL _needsUpdateOfProminentIdentifier;
|
|
92
118
|
}
|
|
93
119
|
|
|
94
120
|
- (instancetype)init
|
|
95
121
|
{
|
|
96
122
|
if (self = [super init]) {
|
|
97
123
|
_tabScreenControllers = nil;
|
|
124
|
+
_installedScreenControllers = nil;
|
|
98
125
|
_tabBarAppearanceCoordinator = [RNSTabBarAppearanceCoordinator new];
|
|
126
|
+
_tabBarItemCoordinator = [RNSTabBarItemCoordinator new];
|
|
99
127
|
_tabsHostComponentView = nil;
|
|
100
128
|
_navigationState = nil;
|
|
101
129
|
_pendingStateUpdate = nil;
|
|
102
130
|
_shouldProgressStateOnMoreNavigationControllerPush = NO;
|
|
131
|
+
_isApplyingProgrammaticTabSelection = NO;
|
|
132
|
+
_pendingMoreTabSelectedEmit = NO;
|
|
133
|
+
_prominentScreenKey = nil;
|
|
134
|
+
_needsUpdateOfProminentIdentifier = NO;
|
|
103
135
|
_observerRegistry = [RNSTabsNavigationStateObserverRegistry new];
|
|
104
136
|
_parentContainerRegistry = [RNSParentContainerItemRegistry new];
|
|
105
137
|
|
|
@@ -156,9 +188,9 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
156
188
|
|
|
157
189
|
- (nullable UIScrollView *)resolveCurrentContentScrollView
|
|
158
190
|
{
|
|
159
|
-
//
|
|
191
|
+
// The effective selection may be the `moreNavigationController` (a `UINavigationController`) -
|
|
160
192
|
// we only resolve for our own tab screens.
|
|
161
|
-
UIViewController *selectedController = self
|
|
193
|
+
UIViewController *selectedController = [self effectiveSelectedViewController];
|
|
162
194
|
if (![selectedController isKindOfClass:RNSTabsScreenViewController.class]) {
|
|
163
195
|
return nil;
|
|
164
196
|
}
|
|
@@ -191,7 +223,32 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
191
223
|
|
|
192
224
|
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item
|
|
193
225
|
{
|
|
194
|
-
|
|
226
|
+
#if RNS_MORE_NAVIGATION_CONTROLLER_AVAILABLE && RNS_UITAB_API_SDK_AVAILABLE
|
|
227
|
+
if (RNS_UITAB_API_ENABLED) {
|
|
228
|
+
// The only direct "user tapped More" signal on the UITab path - no UITab delegate covers More.
|
|
229
|
+
// Mirrors the More branch of the legacy `shouldSelectViewController:`: enforce selection
|
|
230
|
+
// prevention on the More stack top before UIKit displays it.
|
|
231
|
+
if ([self isMoreNavigationControllerPresentInTabBar] && item == self.moreNavigationController.tabBarItem) {
|
|
232
|
+
[self prepareForMoreNavigationControllerHandlingIfNeeded];
|
|
233
|
+
[self disableNavigationBarInMoreNavigationController];
|
|
234
|
+
UIViewController *_Nullable poppedViewController =
|
|
235
|
+
[self popToRootInMoreNavigationControllerRespectSelectionPrevention:YES animated:NO];
|
|
236
|
+
if (poppedViewController != nil) {
|
|
237
|
+
[self
|
|
238
|
+
onDidPreventUserFromSelectingViewControllerWithKey:[self screenKeyForViewController:poppedViewController]];
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// At this point, the `view.selectedViewController` is not yet updated and points
|
|
242
|
+
// to previous tab's viewController, which allows us to tell if whether
|
|
243
|
+
// we've just navigated to More tab or we've been there earlier
|
|
244
|
+
// Verified on both iOS 26 and 27. There's no other simple way to check that
|
|
245
|
+
// since `didSelectTab:` doesn't fire for More tab
|
|
246
|
+
if (![self isSelectedViewControllerTheMoreNavigationController]) {
|
|
247
|
+
_pendingMoreTabSelectedEmit = YES;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
#endif // RNS_MORE_NAVIGATION_CONTROLLER_AVAILABLE && RNS_UITAB_API_SDK_AVAILABLE
|
|
195
252
|
}
|
|
196
253
|
|
|
197
254
|
- (void)setSelectedIndex:(NSUInteger)selectedIndex
|
|
@@ -210,16 +267,27 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
210
267
|
}
|
|
211
268
|
}
|
|
212
269
|
|
|
270
|
+
#if RNS_UITAB_API_SDK_AVAILABLE
|
|
271
|
+
- (void)setSelectedTab:(UITab *)selectedTab API_AVAILABLE(ios(18.0))
|
|
272
|
+
{
|
|
273
|
+
[super setSelectedTab:selectedTab];
|
|
274
|
+
if (!_isHandlingExplicitSelectionUpdate) {
|
|
275
|
+
[self reconcileNavigationStateWithUIKitState];
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
#endif // RNS_UITAB_API_SDK_AVAILABLE
|
|
279
|
+
|
|
213
280
|
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
|
|
214
281
|
{
|
|
215
282
|
[super traitCollectionDidChange:previousTraitCollection];
|
|
216
283
|
|
|
217
|
-
|
|
284
|
+
UIViewController *_Nullable selectedController = [self effectiveSelectedViewController];
|
|
285
|
+
if (previousTraitCollection == nil || selectedController == nil) {
|
|
218
286
|
return;
|
|
219
287
|
}
|
|
220
288
|
|
|
221
289
|
if (self.traitCollection.horizontalSizeClass != previousTraitCollection.horizontalSizeClass &&
|
|
222
|
-
[self isViewControllerHostedByMoreNavigationController:
|
|
290
|
+
[self isViewControllerHostedByMoreNavigationController:selectedController]) {
|
|
223
291
|
[self disableNavigationBarInMoreNavigationController];
|
|
224
292
|
}
|
|
225
293
|
}
|
|
@@ -275,12 +343,16 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
275
343
|
- (void)performContainerUpdate
|
|
276
344
|
{
|
|
277
345
|
_isHandlingExplicitSelectionUpdate = YES;
|
|
346
|
+
[self createTabBarItemsIfNeeded];
|
|
278
347
|
[self updateChildViewControllersIfNeeded];
|
|
348
|
+
[self updateSearchTabsIfNeeded];
|
|
279
349
|
[self updateSelectedViewControllerIfNeeded];
|
|
280
350
|
_isHandlingExplicitSelectionUpdate = NO;
|
|
281
351
|
|
|
352
|
+
[self updateTabBarItemsIfNeeded];
|
|
282
353
|
[self updateTabBarAppearanceIfNeeded];
|
|
283
354
|
[self updateTabBarA11yIfNeeded];
|
|
355
|
+
[self updateProminentIdentifierIfNeeded];
|
|
284
356
|
[self updateOrientationIfNeeded];
|
|
285
357
|
}
|
|
286
358
|
|
|
@@ -302,18 +374,15 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
302
374
|
return NO;
|
|
303
375
|
}
|
|
304
376
|
|
|
305
|
-
UIViewController *currSelectedViewController = self.selectedViewController;
|
|
306
|
-
|
|
307
377
|
RCTAssert(![NSString rnscreens_isBlankOrNull:screenKey],
|
|
308
378
|
@"[RNScreens] The screenKey MUST NOT be null if the view controller is not null");
|
|
309
379
|
|
|
310
380
|
[self progressNavigationState:screenKey withOrigin:actionOrigin];
|
|
311
381
|
|
|
312
|
-
if (
|
|
313
|
-
|
|
382
|
+
if (![self isScreenControllerCurrentlySelected:nextSelectedViewController]) {
|
|
383
|
+
[self applySelectedScreenController:nextSelectedViewController];
|
|
314
384
|
}
|
|
315
385
|
|
|
316
|
-
[self setSelectedViewController:nextSelectedViewController];
|
|
317
386
|
return YES;
|
|
318
387
|
}
|
|
319
388
|
|
|
@@ -325,30 +394,22 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
325
394
|
*/
|
|
326
395
|
- (void)updateNavigationStateOnModelUpdate
|
|
327
396
|
{
|
|
328
|
-
|
|
397
|
+
UIViewController *viewController = [self effectiveSelectedViewController];
|
|
398
|
+
[self progressNavigationState:[self screenKeyForViewController:viewController] withOrigin:RNSTabsActionOriginUser];
|
|
329
399
|
}
|
|
330
400
|
|
|
331
|
-
- (void)
|
|
401
|
+
- (void)userDidRepeatSelectionOfScreenController:(nonnull RNSTabsScreenViewController *)screenController
|
|
332
402
|
{
|
|
333
|
-
RCTAssert(self
|
|
334
|
-
@"[RNScreens] Expected
|
|
335
|
-
|
|
336
|
-
if ([self isSelectedViewControllerTheMoreNavigationController]) {
|
|
337
|
-
// We don't want to run neither state update nor side effects.
|
|
338
|
-
return;
|
|
339
|
-
}
|
|
403
|
+
RCTAssert([self isScreenControllerCurrentlySelected:screenController],
|
|
404
|
+
@"[RNScreens] Expected the repeated screen controller to be the current selection");
|
|
340
405
|
|
|
341
|
-
[self
|
|
406
|
+
[self progressNavigationState:[self screenKeyForViewController:screenController] withOrigin:RNSTabsActionOriginUser];
|
|
342
407
|
|
|
343
408
|
// After state progression we trigger the special effect.
|
|
344
|
-
BOOL repeatedSelectionHandledBySpecialEffect = [
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
isRepeated:YES
|
|
349
|
-
hasTriggeredSpecialEffect:repeatedSelectionHandledBySpecialEffect
|
|
350
|
-
actionOrigin:RNSTabsActionOriginUser];
|
|
351
|
-
[_observerRegistry emitDidUpdateStateTo:_navigationState withContext:updateContext sender:self];
|
|
409
|
+
BOOL repeatedSelectionHandledBySpecialEffect = [screenController tabScreenSelectedRepeatedly];
|
|
410
|
+
[self emitSelectionUpdateWithOrigin:RNSTabsActionOriginUser
|
|
411
|
+
repeated:YES
|
|
412
|
+
hasTriggeredSpecialEffect:repeatedSelectionHandledBySpecialEffect];
|
|
352
413
|
}
|
|
353
414
|
|
|
354
415
|
- (void)userDidSelectViewController:(nonnull UIViewController *)viewController
|
|
@@ -357,7 +418,7 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
357
418
|
RCTAssert(self.selectedViewController == viewController,
|
|
358
419
|
@"[RNScreens] Expected UIKit to update selectedViewController");
|
|
359
420
|
|
|
360
|
-
if ([self isSelectedViewControllerTheMoreNavigationController]) {
|
|
421
|
+
if (![self usesUITabAPI] && [self isSelectedViewControllerTheMoreNavigationController]) {
|
|
361
422
|
[self disableNavigationBarInMoreNavigationController];
|
|
362
423
|
[self prepareForMoreNavigationControllerHandlingIfNeeded];
|
|
363
424
|
|
|
@@ -366,14 +427,21 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
366
427
|
[_observerRegistry emitDidSelectMoreTabWithCurrentState:_navigationState sender:self];
|
|
367
428
|
} else {
|
|
368
429
|
[self updateNavigationStateOnModelUpdate];
|
|
369
|
-
|
|
370
|
-
isRepeated:NO
|
|
371
|
-
hasTriggeredSpecialEffect:NO
|
|
372
|
-
actionOrigin:RNSTabsActionOriginUser];
|
|
373
|
-
[_observerRegistry emitDidUpdateStateTo:_navigationState withContext:updateContext sender:self];
|
|
430
|
+
[self emitSelectionUpdateWithOrigin:RNSTabsActionOriginUser repeated:NO hasTriggeredSpecialEffect:NO];
|
|
374
431
|
}
|
|
375
432
|
}
|
|
376
433
|
|
|
434
|
+
- (void)emitSelectionUpdateWithOrigin:(RNSTabsActionOrigin)actionOrigin
|
|
435
|
+
repeated:(BOOL)repeated
|
|
436
|
+
hasTriggeredSpecialEffect:(BOOL)hasTriggeredSpecialEffect
|
|
437
|
+
{
|
|
438
|
+
auto *updateContext = [[RNSTabsNavigationStateUpdateContext alloc] initWithNavState:_navigationState
|
|
439
|
+
isRepeated:repeated
|
|
440
|
+
hasTriggeredSpecialEffect:hasTriggeredSpecialEffect
|
|
441
|
+
actionOrigin:actionOrigin];
|
|
442
|
+
[_observerRegistry emitDidUpdateStateTo:_navigationState withContext:updateContext sender:self];
|
|
443
|
+
}
|
|
444
|
+
|
|
377
445
|
- (void)onDidPreventUserFromSelectingViewControllerWithKey:(nonnull NSString *)screenKey
|
|
378
446
|
{
|
|
379
447
|
[_observerRegistry emitPreventedSelectionOf:screenKey currentState:_navigationState sender:self];
|
|
@@ -390,10 +458,38 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
390
458
|
return screenViewController.isPreventNativeSelectionEnabled;
|
|
391
459
|
}
|
|
392
460
|
|
|
393
|
-
|
|
461
|
+
/// Shared handling for the `shouldSelect` delegate callbacks - the legacy
|
|
462
|
+
/// `tabBarController:shouldSelectViewController:` and the `UITab` `tabBarController:shouldSelectTab:`.
|
|
463
|
+
- (BOOL)interceptUserSelectionOfViewController:(nonnull UIViewController *)viewController
|
|
464
|
+
{
|
|
465
|
+
BOOL repeatedSelection = viewController == self.selectedViewController;
|
|
466
|
+
#if RNS_UITAB_API_SDK_AVAILABLE
|
|
467
|
+
if (RNS_UITAB_API_ENABLED) {
|
|
468
|
+
repeatedSelection = viewController.tab == self.selectedTab && ![self isMoreNavigationControllerTabBarItemSelected];
|
|
469
|
+
}
|
|
470
|
+
#endif // RNS_UITAB_API_SDK_AVAILABLE
|
|
471
|
+
|
|
472
|
+
if (repeatedSelection) {
|
|
473
|
+
// On repeated selection we block the native *pop to root* effect (works from iOS 26) that
|
|
474
|
+
// interferes with our implementation (necessary for controlled tabs). The did-select callback
|
|
475
|
+
// won't fire on a blocked selection, so we trigger the state update here.
|
|
476
|
+
if (![self isViewControllerTheMoreNavigationController:viewController]) {
|
|
477
|
+
[self userDidRepeatSelectionOfScreenController:static_cast<RNSTabsScreenViewController *>(viewController)];
|
|
478
|
+
}
|
|
479
|
+
return YES;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if ([self shouldPreventNativeTabSelection:viewController]) {
|
|
483
|
+
// Ideally we'd call this AFTER we prevent, but there is no appropriate callback. As long as we
|
|
484
|
+
// emit the event asynchronously this is rather fine.
|
|
485
|
+
[self onDidPreventUserFromSelectingViewControllerWithKey:[self screenKeyForViewController:viewController]];
|
|
486
|
+
return YES;
|
|
487
|
+
}
|
|
394
488
|
|
|
395
|
-
|
|
396
|
-
|
|
489
|
+
return NO;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
#pragma mark - UITabBarControllerDelegate
|
|
397
493
|
|
|
398
494
|
- (BOOL)tabBarController:(UITabBarController *)tabBarController
|
|
399
495
|
shouldSelectViewController:(UIViewController *)viewController
|
|
@@ -408,25 +504,8 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
408
504
|
|
|
409
505
|
// TODO: handle enforcing orientation with natively-driven tabs
|
|
410
506
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
if (repeatedSelection) {
|
|
415
|
-
// On repeated selection we return false to prevent native *pop to root* effect that works only starting from iOS 26
|
|
416
|
-
// and interferes with our implementation (which is necessary for controlled tabs).
|
|
417
|
-
|
|
418
|
-
// We trigger the state update from here, because `tabBarController:didSelectViewController:` won't be called.
|
|
419
|
-
[self userDidRepeatViewControllerSelection:viewController];
|
|
420
|
-
|
|
421
|
-
return NO;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
BOOL shouldPreventTabSelection = [self shouldPreventNativeTabSelection:viewController];
|
|
425
|
-
|
|
426
|
-
if (shouldPreventTabSelection) {
|
|
427
|
-
// Ideally we'd call this AFTER we prevent, but there is no appropriate callback.
|
|
428
|
-
// As long as we emit the event asynchronously this is rather fine.
|
|
429
|
-
[self onDidPreventUserFromSelectingViewControllerWithKey:[self screenKeyForViewController:viewController]];
|
|
507
|
+
BOOL selectionWasIntercepted = [self interceptUserSelectionOfViewController:viewController];
|
|
508
|
+
if (selectionWasIntercepted) {
|
|
430
509
|
return NO;
|
|
431
510
|
}
|
|
432
511
|
|
|
@@ -435,7 +514,8 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
435
514
|
// In such case, we want to pop to root.
|
|
436
515
|
// We do it here, because in `tabBarController:didSelectViewController:` we won't receive
|
|
437
516
|
// `moreNavigationController` in case there is already a tab pushed on the stack.
|
|
438
|
-
|
|
517
|
+
// Legacy path only - on the UITab path More never arrives here; `tabBar:didSelectItem:` covers it.
|
|
518
|
+
if (![self usesUITabAPI] && [self isViewControllerTheMoreNavigationController:viewController]) {
|
|
439
519
|
auto *poppedViewController = [self popToRootInMoreNavigationControllerRespectSelectionPrevention:YES animated:NO];
|
|
440
520
|
if (poppedViewController != nil) {
|
|
441
521
|
// We actually popped something -> let's notify JS realm of this fact.
|
|
@@ -462,7 +542,223 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
462
542
|
_isHandlingExplicitSelectionUpdate = NO;
|
|
463
543
|
}
|
|
464
544
|
|
|
465
|
-
#
|
|
545
|
+
#if RNS_UITAB_API_SDK_AVAILABLE
|
|
546
|
+
|
|
547
|
+
#pragma mark UITabBarControllerDelegate (UITab API)
|
|
548
|
+
|
|
549
|
+
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectTab:(UITab *)tab API_AVAILABLE(ios(18.0))
|
|
550
|
+
{
|
|
551
|
+
return [self tabBarController:tabBarController shouldSelectViewController:tab.viewController];
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
- (void)tabBarController:(UITabBarController *)tabBarController
|
|
555
|
+
didSelectTab:(UITab *)selectedTab
|
|
556
|
+
previousTab:(nullable UITab *)previousTab API_AVAILABLE(ios(18.0))
|
|
557
|
+
{
|
|
558
|
+
if (_isApplyingProgrammaticTabSelection) {
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
RCTAssert(self.selectedTab == selectedTab, @"[RNScreens] Expected UIKit to update selectedTab");
|
|
563
|
+
[self tabBarController:tabBarController didSelectViewController:selectedTab.viewController];
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
#endif // RNS_UITAB_API_SDK_AVAILABLE
|
|
567
|
+
|
|
568
|
+
#pragma mark - UIKit configuration boundary
|
|
569
|
+
|
|
570
|
+
// Every UIKit read/write related to child installation & selection goes through the methods below.
|
|
571
|
+
// The legacy `viewControllers`-based API and the `UITab`-based API (iOS 26.1+) are mutually
|
|
572
|
+
// exclusive - funnelling the accesses through a single boundary is what lets the rest of the
|
|
573
|
+
// controller stay path-agnostic.
|
|
574
|
+
|
|
575
|
+
- (void)installScreenControllers:(nonnull NSArray<RNSTabsScreenViewController *> *)screenControllers
|
|
576
|
+
animated:(BOOL)animated
|
|
577
|
+
{
|
|
578
|
+
_installedScreenControllers = screenControllers;
|
|
579
|
+
|
|
580
|
+
#if RNS_UITAB_API_SDK_AVAILABLE
|
|
581
|
+
if (RNS_UITAB_API_ENABLED) {
|
|
582
|
+
UITab *_Nullable previouslySelectedTab = self.selectedTab;
|
|
583
|
+
|
|
584
|
+
// Restoring the stale `selectedTab` while More is active would yank the selection away.
|
|
585
|
+
BOOL shouldRestoreSelectedTab =
|
|
586
|
+
previouslySelectedTab != nil && ![self isMoreNavigationControllerTabBarItemSelected];
|
|
587
|
+
|
|
588
|
+
NSMutableArray<__kindof UITab *> *tabs = [NSMutableArray arrayWithCapacity:screenControllers.count];
|
|
589
|
+
for (RNSTabsScreenViewController *screenController in screenControllers) {
|
|
590
|
+
[tabs addObject:[self tabForTabScreenController:screenController]];
|
|
591
|
+
}
|
|
592
|
+
_isApplyingProgrammaticTabSelection = YES;
|
|
593
|
+
[self setTabs:tabs animated:animated];
|
|
594
|
+
[self assertAtMostOneSearchTabInstalled];
|
|
595
|
+
|
|
596
|
+
if (shouldRestoreSelectedTab && [tabs containsObject:previouslySelectedTab]) {
|
|
597
|
+
self.selectedTab = previouslySelectedTab;
|
|
598
|
+
}
|
|
599
|
+
_isApplyingProgrammaticTabSelection = NO;
|
|
600
|
+
|
|
601
|
+
// The prominent screen key might point to a tab created in this update - re-resolve it.
|
|
602
|
+
_needsUpdateOfProminentIdentifier = YES;
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
#endif // RNS_UITAB_API_SDK_AVAILABLE
|
|
606
|
+
|
|
607
|
+
[self setViewControllers:screenControllers animated:animated];
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
#if RNS_UITAB_API_SDK_AVAILABLE
|
|
611
|
+
/// Debug-build sanity check run after the tabs are installed: UIKit gives only the system search
|
|
612
|
+
/// role special placement & behavior, so more than one `role: 'search'` screen is a config error.
|
|
613
|
+
- (void)assertAtMostOneSearchTabInstalled API_AVAILABLE(ios(18.0))
|
|
614
|
+
{
|
|
615
|
+
#if RCT_DEBUG
|
|
616
|
+
NSUInteger searchTabCount = 0;
|
|
617
|
+
for (UITab *tab in self.tabs) {
|
|
618
|
+
if ([tab isKindOfClass:UISearchTab.class]) {
|
|
619
|
+
searchTabCount += 1;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
RCTAssert(searchTabCount <= 1,
|
|
623
|
+
@"[RNScreens] At most one tab screen can have `role: 'search'`, got %lu",
|
|
624
|
+
(unsigned long)searchTabCount);
|
|
625
|
+
#endif // RCT_DEBUG
|
|
626
|
+
}
|
|
627
|
+
#endif // RNS_UITAB_API_SDK_AVAILABLE
|
|
628
|
+
|
|
629
|
+
// Controllers currently installed in UIKit, not including more controller.
|
|
630
|
+
- (nonnull NSArray<RNSTabsScreenViewController *> *)installedScreenControllers
|
|
631
|
+
{
|
|
632
|
+
#if RNS_UITAB_API_SDK_AVAILABLE
|
|
633
|
+
if (RNS_UITAB_API_ENABLED) {
|
|
634
|
+
return _installedScreenControllers ?: @[];
|
|
635
|
+
}
|
|
636
|
+
#endif // RNS_UITAB_API_SDK_AVAILABLE
|
|
637
|
+
return self.viewControllers ?: @[];
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
- (void)applySelectedScreenController:(nonnull UIViewController *)screenController
|
|
641
|
+
{
|
|
642
|
+
#if RNS_UITAB_API_SDK_AVAILABLE
|
|
643
|
+
if (RNS_UITAB_API_ENABLED) {
|
|
644
|
+
// Both writes below are our own. UIKit sends `didSelectTab:` synchronously from inside them, and
|
|
645
|
+
// `didSelectTab:` must not treat that as a user selection - otherwise the state is progressed
|
|
646
|
+
// twice and a second, `user`-origin event is emitted.
|
|
647
|
+
_isApplyingProgrammaticTabSelection = YES;
|
|
648
|
+
if (![self isMoreNavigationControllerTabBarItemSelected]) {
|
|
649
|
+
RCTAssert(screenController.tab != nil,
|
|
650
|
+
@"[RNScreens] No installed UITab for screenKey: %@",
|
|
651
|
+
[self screenKeyForViewController:screenController]);
|
|
652
|
+
self.selectedTab = screenController.tab;
|
|
653
|
+
} else {
|
|
654
|
+
// While More is active `selectedTab` lags behind UIKit's selection, so the write goes through
|
|
655
|
+
// the legacy setter (our own override, so that reconciliation of implicit UIKit-driven updates
|
|
656
|
+
// keeps working).
|
|
657
|
+
[self setSelectedViewController:screenController];
|
|
658
|
+
}
|
|
659
|
+
_isApplyingProgrammaticTabSelection = NO;
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
#endif // RNS_UITAB_API_SDK_AVAILABLE
|
|
663
|
+
|
|
664
|
+
// Goes through our own setter override, so that reconciliation of implicit UIKit-driven
|
|
665
|
+
// updates keeps working.
|
|
666
|
+
[self setSelectedViewController:screenController];
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/// Whether `screenController` is already the effective current selection.
|
|
670
|
+
- (BOOL)isScreenControllerCurrentlySelected:(nonnull UIViewController *)screenController
|
|
671
|
+
{
|
|
672
|
+
if ([self isMoreNavigationControllerTabBarItemSelected]) {
|
|
673
|
+
return screenController == [self resolveMoreNavigationController].topViewController;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
#if RNS_UITAB_API_SDK_AVAILABLE
|
|
677
|
+
if (RNS_UITAB_API_ENABLED) {
|
|
678
|
+
return screenController == self.selectedTab.viewController;
|
|
679
|
+
}
|
|
680
|
+
#endif // RNS_UITAB_API_SDK_AVAILABLE
|
|
681
|
+
return screenController == self.selectedViewController;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/// Effective current selection for reads, path-agnostic. On the UITab path `selectedViewController`
|
|
685
|
+
/// is not the source of truth and `selectedTab` lags behind while More is active, so More is
|
|
686
|
+
/// resolved explicitly: the hosted screen when one is displayed, the More navigation controller
|
|
687
|
+
/// itself otherwise.
|
|
688
|
+
- (nullable UIViewController *)effectiveSelectedViewController
|
|
689
|
+
{
|
|
690
|
+
if ([self isMoreNavigationControllerTabBarItemSelected]) {
|
|
691
|
+
UINavigationController *moreNavigationController = [self resolveMoreNavigationController];
|
|
692
|
+
UIViewController *_Nullable topViewController = moreNavigationController.topViewController;
|
|
693
|
+
return [topViewController isKindOfClass:RNSTabsScreenViewController.class] ? topViewController
|
|
694
|
+
: moreNavigationController;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
#if RNS_UITAB_API_SDK_AVAILABLE
|
|
698
|
+
if (RNS_UITAB_API_ENABLED) {
|
|
699
|
+
return self.selectedTab.viewController;
|
|
700
|
+
}
|
|
701
|
+
#endif // RNS_UITAB_API_SDK_AVAILABLE
|
|
702
|
+
return self.selectedViewController;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
#if RNS_UITAB_API_SDK_AVAILABLE
|
|
706
|
+
|
|
707
|
+
- (UITab *)tabForTabScreenController:(RNSTabsScreenViewController *)screenController API_AVAILABLE(ios(18.0))
|
|
708
|
+
{
|
|
709
|
+
// Return existing tab if present. Note that this "latches"
|
|
710
|
+
// the type of the tab: you cannot swap regular UITab with UISearchTab;
|
|
711
|
+
// and we want it this way because tabs hold view controller references
|
|
712
|
+
// and UIKit asserts they are unique even for the detached, not-deallocated tabs
|
|
713
|
+
if (screenController.tab) {
|
|
714
|
+
return screenController.tab;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
UITab *tab = [self makeTabForTabScreenController:screenController];
|
|
718
|
+
[self disableUserCustomizationOfTab:tab];
|
|
719
|
+
return tab;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/// Customizable tab would force the Edit button into the iPad sidebar,
|
|
723
|
+
/// and we don't support tab editing as of now.
|
|
724
|
+
- (void)disableUserCustomizationOfTab:(UITab *)tab API_AVAILABLE(ios(18.0))
|
|
725
|
+
{
|
|
726
|
+
tab.preferredPlacement = UITabPlacementFixed;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/// Always builds a FRESH instance, seeded with current content - the bar reads a `UITab` only
|
|
730
|
+
/// when its instance enters the tabs array.
|
|
731
|
+
- (UITab *)makeTabForTabScreenController:(RNSTabsScreenViewController *)screenController API_AVAILABLE(ios(18.0))
|
|
732
|
+
{
|
|
733
|
+
__weak RNSTabsScreenViewController *weakScreenController = screenController;
|
|
734
|
+
|
|
735
|
+
if (screenController.tabScreenComponentView.tabRole == RNSTabsScreenTabRoleSearch) {
|
|
736
|
+
// The designated initializer of `UISearchTab` takes no identifier - UIKit assigns a system one.
|
|
737
|
+
UISearchTab *searchTab = [[UISearchTab alloc] initWithViewControllerProvider:^UIViewController *(UITab *) {
|
|
738
|
+
return weakScreenController;
|
|
739
|
+
}];
|
|
740
|
+
#if RNS_UITAB_API_SDK_AVAILABLE && RNS_IPHONE_OS_VERSION_AVAILABLE(26_0) && !TARGET_OS_TV && !TARGET_OS_VISION
|
|
741
|
+
if (@available(iOS 26.0, *)) {
|
|
742
|
+
if (RNS_UITAB_API_ENABLED) {
|
|
743
|
+
searchTab.automaticallyActivatesSearch = screenController.tabScreenComponentView.automaticallyActivatesSearch;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
#endif // RNS_UITAB_API_SDK_AVAILABLE && RNS_IPHONE_OS_VERSION_AVAILABLE(26_0) && !TARGET_OS_TV && !TARGET_OS_VISION
|
|
747
|
+
|
|
748
|
+
return searchTab;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
return [[UITab alloc] initWithTitle:[screenController.tabScreenComponentView title] ?: @""
|
|
752
|
+
image:nil
|
|
753
|
+
identifier:[screenController.tabScreenComponentView screenKey]
|
|
754
|
+
viewControllerProvider:^UIViewController *(UITab *) {
|
|
755
|
+
return weakScreenController;
|
|
756
|
+
}];
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
#endif // RNS_UITAB_API_SDK_AVAILABLE
|
|
760
|
+
|
|
761
|
+
#pragma mark - UINavigationControllerDelegate - for More controller
|
|
466
762
|
|
|
467
763
|
- (void)navigationController:(UINavigationController *)navigationController
|
|
468
764
|
willShowViewController:(UIViewController *)viewController
|
|
@@ -478,10 +774,55 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
478
774
|
[self shouldProgressStateOnMoreNavigationControllerPush]) {
|
|
479
775
|
[self userDidSelectViewController:viewController];
|
|
480
776
|
[self setShouldProgressStateOnMoreNavigationControllerPush:NO];
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
if ([self usesUITabAPI]) {
|
|
781
|
+
[self handleMoreNavigationControllerTransitionToViewController:viewController];
|
|
481
782
|
}
|
|
482
783
|
#endif // RNS_MORE_NAVIGATION_CONTROLLER_AVAILABLE
|
|
483
784
|
}
|
|
484
785
|
|
|
786
|
+
- (void)handleMoreNavigationControllerTransitionToViewController:(nonnull UIViewController *)viewController
|
|
787
|
+
{
|
|
788
|
+
if ([viewController isKindOfClass:RNSTabsScreenViewController.class]) {
|
|
789
|
+
// A screen retained on the More stack is being re-displayed - not a More list entry.
|
|
790
|
+
_pendingMoreTabSelectedEmit = NO;
|
|
791
|
+
[self progressStateOnMoreHostedScreenRedisplayIfNeeded:viewController];
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// Transition to the More list root.
|
|
796
|
+
if (_pendingMoreTabSelectedEmit) {
|
|
797
|
+
_pendingMoreTabSelectedEmit = NO;
|
|
798
|
+
[_observerRegistry emitDidSelectMoreTabWithCurrentState:_navigationState sender:self];
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
- (void)progressStateOnMoreHostedScreenRedisplayIfNeeded:(nonnull UIViewController *)viewController
|
|
803
|
+
{
|
|
804
|
+
if (_navigationState == nil || _isHandlingExplicitSelectionUpdate ||
|
|
805
|
+
![viewController isKindOfClass:RNSTabsScreenViewController.class]) {
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
// Also skips iOS 27's stale willShow fired for the OUTGOING hosted screen when the selection
|
|
810
|
+
// moves away from More - the bar item flips synchronously before that willShow arrives.
|
|
811
|
+
if (![self isMoreNavigationControllerTabBarItemSelected]) {
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
NSString *screenKey = [self screenKeyForViewController:viewController];
|
|
816
|
+
if ([_navigationState.selectedScreenKey isEqualToString:screenKey]) {
|
|
817
|
+
// Already reflected in the state, e.g. a programmatic selection progressed it before the
|
|
818
|
+
// transition fired this callback.
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
[self progressNavigationState:screenKey withOrigin:RNSTabsActionOriginUser];
|
|
823
|
+
[self emitSelectionUpdateWithOrigin:RNSTabsActionOriginUser repeated:NO hasTriggeredSpecialEffect:NO];
|
|
824
|
+
}
|
|
825
|
+
|
|
485
826
|
#pragma mark - Signals related
|
|
486
827
|
|
|
487
828
|
- (void)updateChildViewControllersIfNeeded
|
|
@@ -501,7 +842,7 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
501
842
|
return;
|
|
502
843
|
}
|
|
503
844
|
|
|
504
|
-
[self
|
|
845
|
+
[self installScreenControllers:_tabScreenControllers animated:[self installedScreenControllers].count != 0];
|
|
505
846
|
}
|
|
506
847
|
|
|
507
848
|
- (void)updateSelectedViewControllerIfNeeded
|
|
@@ -513,7 +854,7 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
513
854
|
|
|
514
855
|
- (void)updateSelectedViewController
|
|
515
856
|
{
|
|
516
|
-
if (_pendingStateUpdate == nil || self.
|
|
857
|
+
if (_pendingStateUpdate == nil || [self installedScreenControllers].count == 0) {
|
|
517
858
|
return;
|
|
518
859
|
}
|
|
519
860
|
|
|
@@ -531,10 +872,9 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
531
872
|
{
|
|
532
873
|
RCTAssert(_pendingStateUpdate != nil, @"[RNScreens] Pending update MUST NOT be nil");
|
|
533
874
|
|
|
534
|
-
UIViewController *_Nonnull currSelectedViewController = self.selectedViewController;
|
|
535
|
-
|
|
536
875
|
NSString *_Nonnull nextSelectedViewControllerKey = _pendingStateUpdate.selectedScreenKey;
|
|
537
|
-
|
|
876
|
+
RNSTabsScreenViewController *nextSelectedViewController =
|
|
877
|
+
[self findChildViewControllerForKey:nextSelectedViewControllerKey];
|
|
538
878
|
|
|
539
879
|
RCTAssert(nextSelectedViewController != nil,
|
|
540
880
|
@"[RNScreens] Failed to determine next selected view controller for key: %@",
|
|
@@ -553,9 +893,10 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
553
893
|
return;
|
|
554
894
|
}
|
|
555
895
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
896
|
+
BOOL isRepeatedSelection = [self isScreenControllerCurrentlySelected:nextSelectedViewController];
|
|
897
|
+
|
|
898
|
+
// Programmatic repeat selection is rejected, unless we're during first render.
|
|
899
|
+
if (isRepeatedSelection && _navigationState != nil) {
|
|
559
900
|
[_observerRegistry emitRejectedStateUpdate:_pendingStateUpdate
|
|
560
901
|
currentState:_navigationState
|
|
561
902
|
withReason:RNSTabsNavigationStateRejectionReasonRepeated
|
|
@@ -563,16 +904,14 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
563
904
|
return;
|
|
564
905
|
}
|
|
565
906
|
|
|
566
|
-
// TODO:
|
|
567
|
-
// Should this be called only on JS updates?
|
|
568
|
-
auto *screenViewController = static_cast<RNSTabsScreenViewController *>(nextSelectedViewController);
|
|
907
|
+
// TODO: move the user interface style update to a callback; should it run only on JS updates?
|
|
569
908
|
if (@available(iOS 26.0, *)) {
|
|
570
909
|
// On iOS 26, we need to set user interface style 2 parent views above the tab bar
|
|
571
910
|
// for this prop to take effect.
|
|
572
911
|
self.tabBar.superview.superview.overrideUserInterfaceStyle =
|
|
573
|
-
|
|
912
|
+
nextSelectedViewController.tabScreenComponentView.userInterfaceStyle;
|
|
574
913
|
} else {
|
|
575
|
-
self.tabBar.overrideUserInterfaceStyle =
|
|
914
|
+
self.tabBar.overrideUserInterfaceStyle = nextSelectedViewController.tabScreenComponentView.userInterfaceStyle;
|
|
576
915
|
}
|
|
577
916
|
|
|
578
917
|
RNSLog(@"Change selected view controller to: %@", nextSelectedViewControllerKey);
|
|
@@ -585,15 +924,20 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
585
924
|
}
|
|
586
925
|
|
|
587
926
|
if (hasStateProgressed) {
|
|
588
|
-
|
|
589
|
-
[[RNSTabsNavigationStateUpdateContext alloc] initWithNavState:_navigationState
|
|
590
|
-
isRepeated:NO
|
|
591
|
-
hasTriggeredSpecialEffect:NO
|
|
592
|
-
actionOrigin:_pendingStateUpdate.actionOrigin];
|
|
593
|
-
[_observerRegistry emitDidUpdateStateTo:_navigationState withContext:context sender:self];
|
|
927
|
+
[self emitSelectionUpdateWithOrigin:_pendingStateUpdate.actionOrigin repeated:NO hasTriggeredSpecialEffect:NO];
|
|
594
928
|
}
|
|
595
929
|
}
|
|
596
930
|
|
|
931
|
+
- (void)createTabBarItemsIfNeeded
|
|
932
|
+
{
|
|
933
|
+
[_tabBarItemCoordinator createTabBarItemsForTabScreenControllers:_tabScreenControllers];
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
- (void)updateTabBarItemsIfNeeded
|
|
937
|
+
{
|
|
938
|
+
[_tabBarItemCoordinator updateTabBarItemsForTabScreenControllers:_tabScreenControllers];
|
|
939
|
+
}
|
|
940
|
+
|
|
597
941
|
- (void)updateTabBarAppearanceIfNeeded
|
|
598
942
|
{
|
|
599
943
|
if (_needsUpdateOfTabBarAppearance) {
|
|
@@ -614,8 +958,8 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
614
958
|
|
|
615
959
|
- (void)updateTabBarA11yIfNeeded
|
|
616
960
|
{
|
|
617
|
-
for (
|
|
618
|
-
auto screenView =
|
|
961
|
+
for (RNSTabsScreenViewController *tabViewController in [self installedScreenControllers]) {
|
|
962
|
+
auto screenView = tabViewController.tabScreenComponentView;
|
|
619
963
|
if (!screenView.tabBarItemNeedsA11yUpdate) {
|
|
620
964
|
continue;
|
|
621
965
|
}
|
|
@@ -626,6 +970,77 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
626
970
|
}
|
|
627
971
|
}
|
|
628
972
|
|
|
973
|
+
/**
|
|
974
|
+
* Perform SearchTab-related updates. This should be called before tab selection update has run,
|
|
975
|
+
* otherwise the search activation may not trigger.
|
|
976
|
+
*/
|
|
977
|
+
- (void)updateSearchTabsIfNeeded
|
|
978
|
+
{
|
|
979
|
+
#if RNS_UITAB_API_SDK_AVAILABLE && RNS_IPHONE_OS_VERSION_AVAILABLE(26_0) && !TARGET_OS_TV && !TARGET_OS_VISION
|
|
980
|
+
if (@available(iOS 26.0, *)) {
|
|
981
|
+
if (RNS_UITAB_API_ENABLED) {
|
|
982
|
+
for (UITab *tab in self.tabs) {
|
|
983
|
+
if (![tab isKindOfClass:UISearchTab.class]) {
|
|
984
|
+
continue;
|
|
985
|
+
}
|
|
986
|
+
auto *searchTab = static_cast<UISearchTab *>(tab);
|
|
987
|
+
auto *screenController = static_cast<RNSTabsScreenViewController *>(tab.viewController);
|
|
988
|
+
auto *screenView = screenController.tabScreenComponentView;
|
|
989
|
+
|
|
990
|
+
if (searchTab.automaticallyActivatesSearch != screenView.automaticallyActivatesSearch) {
|
|
991
|
+
searchTab.automaticallyActivatesSearch = screenView.automaticallyActivatesSearch;
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
#endif // RNS_UITAB_API_SDK_AVAILABLE && RNS_IPHONE_OS_VERSION_AVAILABLE(26_0) && !TARGET_OS_TV && !TARGET_OS_VISION
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
#pragma mark - Prominent tab
|
|
1000
|
+
|
|
1001
|
+
- (void)setProminentScreenKey:(nullable NSString *)screenKey
|
|
1002
|
+
{
|
|
1003
|
+
NSString *_Nullable newScreenKey = screenKey.length > 0 ? [screenKey copy] : nil;
|
|
1004
|
+
if (newScreenKey == _prominentScreenKey || [newScreenKey isEqualToString:_prominentScreenKey]) {
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
_prominentScreenKey = newScreenKey;
|
|
1008
|
+
_needsUpdateOfProminentIdentifier = YES;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
/// Resolves `_prominentScreenKey` against the installed tabs & pushes the result to UIKit.
|
|
1012
|
+
- (void)updateProminentIdentifierIfNeeded
|
|
1013
|
+
{
|
|
1014
|
+
if (!_needsUpdateOfProminentIdentifier) {
|
|
1015
|
+
return;
|
|
1016
|
+
}
|
|
1017
|
+
_needsUpdateOfProminentIdentifier = NO;
|
|
1018
|
+
|
|
1019
|
+
#if RNS_IPHONE_OS_VERSION_AVAILABLE(27_0) && !TARGET_OS_TV && !TARGET_OS_VISION
|
|
1020
|
+
if (@available(iOS 27.0, *)) {
|
|
1021
|
+
NSString *_Nullable tabIdentifier = nil;
|
|
1022
|
+
|
|
1023
|
+
if (_prominentScreenKey != nil) {
|
|
1024
|
+
// The lookup goes through the view controller, because `UISearchTab` carries
|
|
1025
|
+
// a system-assigned identifier.
|
|
1026
|
+
UITab *_Nullable tab = [self findChildViewControllerForKey:_prominentScreenKey].tab;
|
|
1027
|
+
if (tab != nil) {
|
|
1028
|
+
tabIdentifier = tab.identifier;
|
|
1029
|
+
} else {
|
|
1030
|
+
RCTLogWarn(@"[RNScreens] prominentScreenKey '%@' does not match the screenKey of any tab screen",
|
|
1031
|
+
_prominentScreenKey);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
if (tabIdentifier == self.prominentTabIdentifier || [tabIdentifier isEqualToString:self.prominentTabIdentifier]) {
|
|
1036
|
+
return;
|
|
1037
|
+
}
|
|
1038
|
+
// Animate only runtime changes, not the initial setup before the tab bar is on screen.
|
|
1039
|
+
[self setProminentTabIdentifier:tabIdentifier animated:self.viewIfLoaded.window != nil];
|
|
1040
|
+
}
|
|
1041
|
+
#endif // RNS_IPHONE_OS_VERSION_AVAILABLE(27_0) && !TARGET_OS_TV && !TARGET_OS_VISION
|
|
1042
|
+
}
|
|
1043
|
+
|
|
629
1044
|
#pragma mark - Utility
|
|
630
1045
|
|
|
631
1046
|
- (nullable RNSTabsScreenViewController *)findChildViewControllerForKey:(nullable NSString *)screenKey
|
|
@@ -633,11 +1048,7 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
633
1048
|
if (screenKey == nil) {
|
|
634
1049
|
return nil;
|
|
635
1050
|
}
|
|
636
|
-
for (
|
|
637
|
-
RCTAssert([viewController isKindOfClass:RNSTabsScreenViewController.class],
|
|
638
|
-
@"[RNScreens] Unexpected type of controller: %@",
|
|
639
|
-
viewController.class);
|
|
640
|
-
auto *screenViewController = static_cast<RNSTabsScreenViewController *>(viewController);
|
|
1051
|
+
for (RNSTabsScreenViewController *screenViewController in [self installedScreenControllers]) {
|
|
641
1052
|
if ([screenViewController.getScreenKeyOrNull isEqualToString:screenKey]) {
|
|
642
1053
|
return screenViewController;
|
|
643
1054
|
}
|
|
@@ -662,16 +1073,14 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
662
1073
|
}
|
|
663
1074
|
}
|
|
664
1075
|
|
|
665
|
-
|
|
666
|
-
* Be sure to call this method IF AND ONLY IF you know that the `self.selectedViewController`
|
|
667
|
-
* is not the `moreNavigationController`.
|
|
668
|
-
*/
|
|
669
|
-
- (RNSTabsScreenViewController *)selectedScreenViewController
|
|
1076
|
+
- (BOOL)usesUITabAPI
|
|
670
1077
|
{
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
1078
|
+
#if RNS_UITAB_API_SDK_AVAILABLE
|
|
1079
|
+
if (RNS_UITAB_API_ENABLED) {
|
|
1080
|
+
return YES;
|
|
1081
|
+
}
|
|
1082
|
+
#endif // RNS_UITAB_API_SDK_AVAILABLE
|
|
1083
|
+
return NO;
|
|
675
1084
|
}
|
|
676
1085
|
|
|
677
1086
|
- (nonnull NSString *)screenKeyForViewController:(nonnull UIViewController *)viewController
|
|
@@ -717,14 +1126,17 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
717
1126
|
return;
|
|
718
1127
|
}
|
|
719
1128
|
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
1129
|
+
UIViewController *_Nullable selectedController = [self effectiveSelectedViewController];
|
|
1130
|
+
if (selectedController == nil) {
|
|
1131
|
+
return;
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
if (![selectedController isKindOfClass:RNSTabsScreenViewController.class]) {
|
|
1135
|
+
RCTAssert(NO, @"[RNScreens] Unexpected controller type during state reconciliation: %@", selectedController.class);
|
|
724
1136
|
return;
|
|
725
1137
|
}
|
|
726
1138
|
|
|
727
|
-
NSString *selectedScreenKey = [self
|
|
1139
|
+
NSString *selectedScreenKey = [self screenKeyForViewController:selectedController];
|
|
728
1140
|
if ([_navigationState.selectedScreenKey isEqualToString:selectedScreenKey]) {
|
|
729
1141
|
return;
|
|
730
1142
|
}
|
|
@@ -738,11 +1150,7 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
738
1150
|
[self disableNavigationBarInMoreNavigationController];
|
|
739
1151
|
}
|
|
740
1152
|
|
|
741
|
-
|
|
742
|
-
isRepeated:NO
|
|
743
|
-
hasTriggeredSpecialEffect:NO
|
|
744
|
-
actionOrigin:RNSTabsActionOriginImplicit];
|
|
745
|
-
[_observerRegistry emitDidUpdateStateTo:_navigationState withContext:context sender:self];
|
|
1153
|
+
[self emitSelectionUpdateWithOrigin:RNSTabsActionOriginImplicit repeated:NO hasTriggeredSpecialEffect:NO];
|
|
746
1154
|
}
|
|
747
1155
|
|
|
748
1156
|
/**
|
|
@@ -769,7 +1177,7 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
769
1177
|
// https://developer.apple.com/documentation/uikit/uitabbarcontroller?language=objc#The-More-navigation-controller
|
|
770
1178
|
// The count is documented. Size class check is empirical, to tighten the condition and have less
|
|
771
1179
|
// false positives. If we ever find it not correct, we can safely remove it.
|
|
772
|
-
return self.
|
|
1180
|
+
return [self installedScreenControllers].count >= kMinCountOfVCsForMoreVCPresence &&
|
|
773
1181
|
self.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact;
|
|
774
1182
|
#else
|
|
775
1183
|
return NO;
|
|
@@ -783,7 +1191,7 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
783
1191
|
}
|
|
784
1192
|
|
|
785
1193
|
// Guard: VC must be one we manage (excludes arbitrary external VCs).
|
|
786
|
-
if ([self
|
|
1194
|
+
if (![[self installedScreenControllers] containsObject:static_cast<RNSTabsScreenViewController *>(viewController)]) {
|
|
787
1195
|
return NO;
|
|
788
1196
|
}
|
|
789
1197
|
|
|
@@ -1054,8 +1462,9 @@ static void rns_pushViewController(__unsafe_unretained id self,
|
|
|
1054
1462
|
|
|
1055
1463
|
- (RNSOrientation)evaluateOrientation
|
|
1056
1464
|
{
|
|
1057
|
-
|
|
1058
|
-
|
|
1465
|
+
UIViewController *_Nullable selectedController = [self effectiveSelectedViewController];
|
|
1466
|
+
if ([selectedController respondsToSelector:@selector(evaluateOrientation)]) {
|
|
1467
|
+
id<RNSOrientationProviding> selected = static_cast<id<RNSOrientationProviding>>(selectedController);
|
|
1059
1468
|
return [selected evaluateOrientation];
|
|
1060
1469
|
}
|
|
1061
1470
|
|