@capgo/capacitor-native-navigation 8.0.10 → 8.0.12

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 CHANGED
@@ -1,5 +1,5 @@
1
1
  # @capgo/capacitor-native-navigation
2
- <a href="https://capgo.app/"><img src="https://raw.githubusercontent.com/Cap-go/capgo/main/assets/capgo_banner.png" alt="Capgo - Instant updates for capacitor" /></a>
2
+ <a href="https://capgo.app/"><img src="https://capgo.app/readme-banner.svg?repo=Cap-go/capacitor-native-navigation" alt="Capgo - Instant updates for Capacitor" /></a>
3
3
 
4
4
  <div align="center">
5
5
  <h2><a href="https://capgo.app/?ref=plugin_native_navigation">Get instant updates for your app with Capgo</a></h2>
@@ -324,7 +324,7 @@ Inline SVG supports the icon-focused subset used by common sets such as Lucide a
324
324
 
325
325
  ## Platform Notes
326
326
 
327
- - iOS uses UIKit `UINavigationBar` and `UITabBar`. iOS 26+ uses Apple's system Liquid Glass with transparent bar appearances and no plugin-supplied background plate; earlier versions use native translucent/material fallback styling.
327
+ - iOS uses UIKit `UINavigationBar` and `UITabBar`. iOS 26+ hosts the tabbar in a real `UITabBarController` so Apple owns the system Liquid Glass background; earlier versions use native translucent/material fallback styling.
328
328
  - Android uses an AppCompat `Toolbar` and a floating native tab capsule with edge-to-edge placement.
329
329
  - Web fallback does not draw native bars; it mirrors inset variables and events for development.
330
330
 
@@ -678,22 +678,23 @@ because icons are rendered by native UI.
678
678
 
679
679
  Native tabbar state.
680
680
 
681
- | Prop | Type | Description |
682
- | -------------------------- | --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
683
- | **`hidden`** | <code>boolean</code> | Hide the native tabbar. |
684
- | **`tabs`** | <code>NativeNavigationTab[]</code> | Tab definitions. |
685
- | **`selectedId`** | <code>string</code> | Currently selected tab id. |
686
- | **`labels`** | <code>boolean</code> | Show text labels. Defaults to `true`. |
687
- | **`labelVisibilityMode`** | <code><a href="#nativenavigationtablabelvisibilitymode">NativeNavigationTabLabelVisibilityMode</a></code> | Native label visibility mode. Overrides `labels` when provided. |
688
- | **`icons`** | <code>boolean</code> | Show icons. Defaults to `true`. |
689
- | **`colors`** | <code><a href="#nativenavigationcolors">NativeNavigationColors</a></code> | Tabbar color hints. |
690
- | **`blurEffect`** | <code><a href="#nativenavigationblureffect">NativeNavigationBlurEffect</a></code> | iOS blur/material effect for the tabbar background when glass is not available. |
691
- | **`disableIndicator`** | <code>boolean</code> | Disable the Android active tab indicator. |
692
- | **`indicatorColor`** | <code>string</code> | Active tab indicator color on Android. `colors.indicator` is also supported. |
693
- | **`rippleColor`** | <code>string</code> | Tab press ripple color on Android. `colors.ripple` is also supported. |
694
- | **`badgeBackgroundColor`** | <code>string</code> | Badge background color. `colors.badgeBackground` is also supported. |
695
- | **`badgeTextColor`** | <code>string</code> | Badge text color. `colors.badgeText` is also supported. |
696
- | **`animated`** | <code>boolean</code> | Animate native tabbar changes. |
681
+ | Prop | Type | Description |
682
+ | ------------------------------------ | --------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
683
+ | **`hidden`** | <code>boolean</code> | Hide the native tabbar. |
684
+ | **`tabs`** | <code>NativeNavigationTab[]</code> | Tab definitions. |
685
+ | **`selectedId`** | <code>string</code> | Currently selected tab id. |
686
+ | **`labels`** | <code>boolean</code> | Show text labels. Defaults to `true`. |
687
+ | **`labelVisibilityMode`** | <code><a href="#nativenavigationtablabelvisibilitymode">NativeNavigationTabLabelVisibilityMode</a></code> | Native label visibility mode. Overrides `labels` when provided. |
688
+ | **`icons`** | <code>boolean</code> | Show icons. Defaults to `true`. |
689
+ | **`colors`** | <code><a href="#nativenavigationcolors">NativeNavigationColors</a></code> | Tabbar color hints. |
690
+ | **`blurEffect`** | <code><a href="#nativenavigationblureffect">NativeNavigationBlurEffect</a></code> | iOS blur/material effect for the tabbar background when glass is not available. |
691
+ | **`disableTransparentOnScrollEdge`** | <code>boolean</code> | Keep the iOS scroll-edge tabbar appearance from becoming transparent. Mirrors Expo Router native tabs' `disableTransparentOnScrollEdge` option. Defaults to `false`. |
692
+ | **`disableIndicator`** | <code>boolean</code> | Disable the Android active tab indicator. |
693
+ | **`indicatorColor`** | <code>string</code> | Active tab indicator color on Android. `colors.indicator` is also supported. |
694
+ | **`rippleColor`** | <code>string</code> | Tab press ripple color on Android. `colors.ripple` is also supported. |
695
+ | **`badgeBackgroundColor`** | <code>string</code> | Badge background color. `colors.badgeBackground` is also supported. |
696
+ | **`badgeTextColor`** | <code>string</code> | Badge text color. `colors.badgeText` is also supported. |
697
+ | **`animated`** | <code>boolean</code> | Animate native tabbar changes. |
697
698
 
698
699
 
699
700
  #### NativeNavigationTab
package/dist/docs.json CHANGED
@@ -738,6 +738,13 @@
738
738
  ],
739
739
  "type": "NativeNavigationBlurEffect"
740
740
  },
741
+ {
742
+ "name": "disableTransparentOnScrollEdge",
743
+ "tags": [],
744
+ "docs": "Keep the iOS scroll-edge tabbar appearance from becoming transparent.\nMirrors Expo Router native tabs' `disableTransparentOnScrollEdge` option.\nDefaults to `false`.",
745
+ "complexTypes": [],
746
+ "type": "boolean | undefined"
747
+ },
741
748
  {
742
749
  "name": "disableIndicator",
743
750
  "tags": [],
@@ -311,6 +311,12 @@ export interface NativeNavigationTabbarOptions {
311
311
  * available.
312
312
  */
313
313
  blurEffect?: NativeNavigationBlurEffect;
314
+ /**
315
+ * Keep the iOS scroll-edge tabbar appearance from becoming transparent.
316
+ * Mirrors Expo Router native tabs' `disableTransparentOnScrollEdge` option.
317
+ * Defaults to `false`.
318
+ */
319
+ disableTransparentOnScrollEdge?: boolean;
314
320
  /**
315
321
  * Disable the Android active tab indicator.
316
322
  */
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import type { PluginListenerHandle } from '@capacitor/core';\n\n/**\n * Platform rendering preference for the native bars.\n */\nexport type NativeNavigationPlatformStyle = 'auto' | 'ios' | 'android';\n\n/**\n * How the plugin exposes native bar sizes to web content.\n */\nexport type NativeNavigationContentInsetMode = 'css' | 'none';\n\n/**\n * Navigation animation direction.\n */\nexport type NativeNavigationTransitionDirection = 'forward' | 'back' | 'root' | 'tab' | 'zoom' | 'none';\n\n/**\n * Native material/blur effect preference.\n */\nexport type NativeNavigationBlurEffect =\n | 'none'\n | 'systemDefault'\n | 'extraLight'\n | 'light'\n | 'dark'\n | 'regular'\n | 'prominent'\n | 'systemUltraThinMaterial'\n | 'systemThinMaterial'\n | 'systemMaterial'\n | 'systemThickMaterial'\n | 'systemChromeMaterial'\n | 'systemUltraThinMaterialLight'\n | 'systemThinMaterialLight'\n | 'systemMaterialLight'\n | 'systemThickMaterialLight'\n | 'systemChromeMaterialLight'\n | 'systemUltraThinMaterialDark'\n | 'systemThinMaterialDark'\n | 'systemMaterialDark'\n | 'systemThickMaterialDark'\n | 'systemChromeMaterialDark';\n\n/**\n * Native tab label visibility behavior.\n */\nexport type NativeNavigationTabLabelVisibilityMode = 'auto' | 'selected' | 'labeled' | 'unlabeled';\n\n/**\n * A rectangle in WebView viewport coordinates, expressed in native points/dp.\n */\nexport interface NativeNavigationRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/**\n * A serializable icon descriptor. Framework nodes are intentionally not accepted\n * because icons are rendered by native UI.\n */\nexport interface NativeNavigationIcon {\n /**\n * Cross-platform asset path or URL fallback.\n */\n src?: string;\n\n /**\n * Cross-platform inline SVG markup. The native renderers support common icon\n * shapes such as path, line, polyline, polygon, circle, and rect. SVG icons\n * are rendered as template images by default so native tint colors still\n * apply.\n */\n svg?: string;\n\n /**\n * Preferred rendered icon width in native points/dp. Defaults to `24`.\n */\n width?: number;\n\n /**\n * Preferred rendered icon height in native points/dp. Defaults to `24`.\n */\n height?: number;\n\n /**\n * When `true`, native tint colors are applied to the rendered SVG/image.\n * Defaults to `true`.\n */\n template?: boolean;\n\n /**\n * iOS-specific SF Symbol, bundled image name, or inline SVG.\n */\n ios?: {\n /**\n * SF Symbol name, for example `house.fill`.\n */\n sfSymbol?: string;\n\n /**\n * Bundled image name from the app asset catalog.\n */\n image?: string;\n\n /**\n * iOS-specific inline SVG markup.\n */\n svg?: string;\n };\n\n /**\n * Android-specific drawable resource, asset name, or inline SVG.\n */\n android?: {\n /**\n * Drawable resource name without the `R.drawable.` prefix.\n */\n resource?: string;\n\n /**\n * Bundled image asset name.\n */\n image?: string;\n\n /**\n * Android-specific inline SVG markup.\n */\n svg?: string;\n };\n}\n\n/**\n * Native bar colors. Use CSS-style hex strings (`#RRGGBB` or `#AARRGGBB`).\n */\nexport interface NativeNavigationColors {\n /**\n * When `true`, Android 12+ derives unspecified bar colors from Material You\n * system palettes. Explicit color fields still win.\n */\n dynamic?: boolean;\n\n /**\n * Tint color for active buttons/items.\n */\n tint?: string;\n\n /**\n * Color for inactive tab items.\n */\n inactiveTint?: string;\n\n /**\n * Optional background tint. Ignored on iOS 26+ so UIKit can preserve the\n * system Liquid Glass navigation appearance.\n */\n background?: string;\n\n /**\n * Title and label text color where the native platform supports it.\n */\n foreground?: string;\n\n /**\n * Badge background color for native tab badges.\n */\n badgeBackground?: string;\n\n /**\n * Badge text color for native tab badges.\n */\n badgeText?: string;\n\n /**\n * Active tab indicator color on Android.\n */\n indicator?: string;\n\n /**\n * Tab press ripple color on Android.\n */\n ripple?: string;\n}\n\n/**\n * Global plugin configuration.\n */\nexport interface NativeNavigationConfigureOptions {\n /**\n * Enables or disables the native chrome host.\n */\n enabled?: boolean;\n\n /**\n * Native style preference. `auto` uses the current platform.\n */\n platformStyle?: NativeNavigationPlatformStyle;\n\n /**\n * When `css`, the plugin writes CSS variables on `document.documentElement`.\n */\n contentInsetMode?: NativeNavigationContentInsetMode;\n\n /**\n * Default native transition duration in milliseconds.\n */\n animationDuration?: number;\n\n /**\n * Shared color hints for native bars.\n */\n colors?: NativeNavigationColors;\n}\n\n/**\n * A button shown in the native navbar.\n */\nexport interface NativeNavigationBarButton {\n /**\n * Stable id returned in `navbarItemTap`.\n */\n id: string;\n\n /**\n * Visible text label.\n */\n title?: string;\n\n /**\n * Native icon descriptor.\n */\n icon?: NativeNavigationIcon;\n\n /**\n * Whether the action is enabled. Defaults to `true`.\n */\n enabled?: boolean;\n}\n\n/**\n * Native back button configuration.\n */\nexport interface NativeNavigationBackButton {\n /**\n * Show the native back affordance.\n */\n visible?: boolean;\n\n /**\n * Optional back title.\n */\n title?: string;\n}\n\n/**\n * Native navbar state.\n */\nexport interface NativeNavigationNavbarOptions {\n /**\n * Hide the native navbar.\n */\n hidden?: boolean;\n\n /**\n * Main title.\n */\n title?: string;\n\n /**\n * Secondary title where supported by the platform.\n */\n subtitle?: string;\n\n /**\n * Prefer a large iOS title style.\n */\n large?: boolean;\n\n /**\n * Prefer transparent/scroll-edge style.\n */\n transparent?: boolean;\n\n /**\n * iOS blur/material effect for the navbar background when glass is not\n * available. Defaults to `systemChromeMaterial` for transparent bars.\n */\n blurEffect?: NativeNavigationBlurEffect;\n\n /**\n * Back button state.\n */\n backButton?: NativeNavigationBackButton;\n\n /**\n * Left-side action buttons.\n */\n leftItems?: NativeNavigationBarButton[];\n\n /**\n * Right-side action buttons.\n */\n rightItems?: NativeNavigationBarButton[];\n\n /**\n * Navbar color hints.\n */\n colors?: NativeNavigationColors;\n\n /**\n * Animate native navbar changes.\n */\n animated?: boolean;\n}\n\n/**\n * A native tab item.\n */\nexport interface NativeNavigationTab {\n /**\n * Stable tab id returned in `tabSelect`.\n */\n id: string;\n\n /**\n * Visible tab label.\n */\n title?: string;\n\n /**\n * Native icon descriptor.\n */\n icon?: NativeNavigationIcon;\n\n /**\n * Optional selected-state icon.\n */\n selectedIcon?: NativeNavigationIcon;\n\n /**\n * Optional badge. Numeric badges are supported on both platforms; text badge\n * support depends on platform capabilities.\n */\n badge?: string | number;\n\n /**\n * Whether the tab is enabled. Defaults to `true`.\n */\n enabled?: boolean;\n}\n\n/**\n * Native tabbar state.\n */\nexport interface NativeNavigationTabbarOptions {\n /**\n * Hide the native tabbar.\n */\n hidden?: boolean;\n\n /**\n * Tab definitions.\n */\n tabs?: NativeNavigationTab[];\n\n /**\n * Currently selected tab id.\n */\n selectedId?: string;\n\n /**\n * Show text labels. Defaults to `true`.\n */\n labels?: boolean;\n\n /**\n * Native label visibility mode. Overrides `labels` when provided.\n */\n labelVisibilityMode?: NativeNavigationTabLabelVisibilityMode;\n\n /**\n * Show icons. Defaults to `true`.\n */\n icons?: boolean;\n\n /**\n * Tabbar color hints.\n */\n colors?: NativeNavigationColors;\n\n /**\n * iOS blur/material effect for the tabbar background when glass is not\n * available.\n */\n blurEffect?: NativeNavigationBlurEffect;\n\n /**\n * Disable the Android active tab indicator.\n */\n disableIndicator?: boolean;\n\n /**\n * Active tab indicator color on Android. `colors.indicator` is also\n * supported.\n */\n indicatorColor?: string;\n\n /**\n * Tab press ripple color on Android. `colors.ripple` is also supported.\n */\n rippleColor?: string;\n\n /**\n * Badge background color. `colors.badgeBackground` is also supported.\n */\n badgeBackgroundColor?: string;\n\n /**\n * Badge text color. `colors.badgeText` is also supported.\n */\n badgeTextColor?: string;\n\n /**\n * Animate native tabbar changes.\n */\n animated?: boolean;\n}\n\n/**\n * Insets exposed to web content.\n */\nexport interface NativeNavigationInsets {\n top: number;\n right: number;\n bottom: number;\n left: number;\n navbarHeight: number;\n tabbarHeight: number;\n}\n\n/**\n * Returned by methods that may change safe content bounds.\n */\nexport interface NativeNavigationInsetsResult {\n insets: NativeNavigationInsets;\n}\n\n/**\n * Begin a native transition transaction before JS changes route content.\n */\nexport interface NativeNavigationBeginTransitionOptions {\n id?: string;\n direction?: NativeNavigationTransitionDirection;\n duration?: number;\n /**\n * Source rectangle for `zoom` transitions. Use viewport coordinates such as\n * those returned by `Element.getBoundingClientRect()`.\n */\n sourceRect?: NativeNavigationRect;\n /**\n * Destination rectangle for shared-element-style `zoom` transitions.\n */\n targetRect?: NativeNavigationRect;\n /**\n * Corner radius used while animating a `zoom` transition.\n */\n cornerRadius?: number;\n}\n\n/**\n * Finish a native transition transaction after JS has changed route content.\n */\nexport interface NativeNavigationFinishTransitionOptions {\n id?: string;\n direction?: NativeNavigationTransitionDirection;\n duration?: number;\n /**\n * Source rectangle for `zoom` transitions when no active source was recorded.\n */\n sourceRect?: NativeNavigationRect;\n /**\n * Destination rectangle for shared-element-style `zoom` transitions.\n */\n targetRect?: NativeNavigationRect;\n /**\n * Corner radius used while animating a `zoom` transition.\n */\n cornerRadius?: number;\n}\n\n/**\n * Native transition result.\n */\nexport interface NativeNavigationTransitionResult {\n id: string;\n direction: NativeNavigationTransitionDirection;\n duration: number;\n}\n\n/**\n * Plugin version payload.\n */\nexport interface PluginVersionResult {\n /**\n * Version identifier returned by the platform implementation.\n */\n version: string;\n}\n\nexport interface NativeNavigationBackEvent {\n source: 'navbar';\n}\n\nexport interface NativeNavigationBarItemTapEvent {\n id: string;\n title?: string;\n placement: 'left' | 'right';\n}\n\nexport interface NativeNavigationTabSelectEvent {\n id: string;\n index: number;\n title?: string;\n}\n\nexport interface NativeNavigationSafeAreaChangedEvent {\n insets: NativeNavigationInsets;\n}\n\nexport interface NativeNavigationTransitionEvent {\n id: string;\n direction: NativeNavigationTransitionDirection;\n duration: number;\n}\n\n/**\n * Framework-agnostic native navigation chrome API.\n */\nexport interface NativeNavigationPlugin {\n /**\n * Configure the native chrome host and content inset behavior.\n */\n configure(options?: NativeNavigationConfigureOptions): Promise<NativeNavigationInsetsResult>;\n\n /**\n * Render or update the native navbar.\n */\n setNavbar(options: NativeNavigationNavbarOptions): Promise<NativeNavigationInsetsResult>;\n\n /**\n * Render or update the native tabbar.\n */\n setTabbar(options: NativeNavigationTabbarOptions): Promise<NativeNavigationInsetsResult>;\n\n /**\n * Capture the current WebView and prepare a native transition.\n */\n beginTransition(options?: NativeNavigationBeginTransitionOptions): Promise<NativeNavigationTransitionResult>;\n\n /**\n * Animate from the captured WebView snapshot to the current live WebView.\n */\n finishTransition(options?: NativeNavigationFinishTransitionOptions): Promise<NativeNavigationTransitionResult>;\n\n /**\n * Returns the platform implementation version marker.\n */\n getPluginVersion(): Promise<PluginVersionResult>;\n\n addListener(\n eventName: 'navbarBack',\n listenerFunc: (event: NativeNavigationBackEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'navbarItemTap',\n listenerFunc: (event: NativeNavigationBarItemTapEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'tabSelect',\n listenerFunc: (event: NativeNavigationTabSelectEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'safeAreaChanged',\n listenerFunc: (event: NativeNavigationSafeAreaChangedEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'transitionStart',\n listenerFunc: (event: NativeNavigationTransitionEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'transitionEnd',\n listenerFunc: (event: NativeNavigationTransitionEvent) => void,\n ): Promise<PluginListenerHandle>;\n}\n"]}
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import type { PluginListenerHandle } from '@capacitor/core';\n\n/**\n * Platform rendering preference for the native bars.\n */\nexport type NativeNavigationPlatformStyle = 'auto' | 'ios' | 'android';\n\n/**\n * How the plugin exposes native bar sizes to web content.\n */\nexport type NativeNavigationContentInsetMode = 'css' | 'none';\n\n/**\n * Navigation animation direction.\n */\nexport type NativeNavigationTransitionDirection = 'forward' | 'back' | 'root' | 'tab' | 'zoom' | 'none';\n\n/**\n * Native material/blur effect preference.\n */\nexport type NativeNavigationBlurEffect =\n | 'none'\n | 'systemDefault'\n | 'extraLight'\n | 'light'\n | 'dark'\n | 'regular'\n | 'prominent'\n | 'systemUltraThinMaterial'\n | 'systemThinMaterial'\n | 'systemMaterial'\n | 'systemThickMaterial'\n | 'systemChromeMaterial'\n | 'systemUltraThinMaterialLight'\n | 'systemThinMaterialLight'\n | 'systemMaterialLight'\n | 'systemThickMaterialLight'\n | 'systemChromeMaterialLight'\n | 'systemUltraThinMaterialDark'\n | 'systemThinMaterialDark'\n | 'systemMaterialDark'\n | 'systemThickMaterialDark'\n | 'systemChromeMaterialDark';\n\n/**\n * Native tab label visibility behavior.\n */\nexport type NativeNavigationTabLabelVisibilityMode = 'auto' | 'selected' | 'labeled' | 'unlabeled';\n\n/**\n * A rectangle in WebView viewport coordinates, expressed in native points/dp.\n */\nexport interface NativeNavigationRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/**\n * A serializable icon descriptor. Framework nodes are intentionally not accepted\n * because icons are rendered by native UI.\n */\nexport interface NativeNavigationIcon {\n /**\n * Cross-platform asset path or URL fallback.\n */\n src?: string;\n\n /**\n * Cross-platform inline SVG markup. The native renderers support common icon\n * shapes such as path, line, polyline, polygon, circle, and rect. SVG icons\n * are rendered as template images by default so native tint colors still\n * apply.\n */\n svg?: string;\n\n /**\n * Preferred rendered icon width in native points/dp. Defaults to `24`.\n */\n width?: number;\n\n /**\n * Preferred rendered icon height in native points/dp. Defaults to `24`.\n */\n height?: number;\n\n /**\n * When `true`, native tint colors are applied to the rendered SVG/image.\n * Defaults to `true`.\n */\n template?: boolean;\n\n /**\n * iOS-specific SF Symbol, bundled image name, or inline SVG.\n */\n ios?: {\n /**\n * SF Symbol name, for example `house.fill`.\n */\n sfSymbol?: string;\n\n /**\n * Bundled image name from the app asset catalog.\n */\n image?: string;\n\n /**\n * iOS-specific inline SVG markup.\n */\n svg?: string;\n };\n\n /**\n * Android-specific drawable resource, asset name, or inline SVG.\n */\n android?: {\n /**\n * Drawable resource name without the `R.drawable.` prefix.\n */\n resource?: string;\n\n /**\n * Bundled image asset name.\n */\n image?: string;\n\n /**\n * Android-specific inline SVG markup.\n */\n svg?: string;\n };\n}\n\n/**\n * Native bar colors. Use CSS-style hex strings (`#RRGGBB` or `#AARRGGBB`).\n */\nexport interface NativeNavigationColors {\n /**\n * When `true`, Android 12+ derives unspecified bar colors from Material You\n * system palettes. Explicit color fields still win.\n */\n dynamic?: boolean;\n\n /**\n * Tint color for active buttons/items.\n */\n tint?: string;\n\n /**\n * Color for inactive tab items.\n */\n inactiveTint?: string;\n\n /**\n * Optional background tint. Ignored on iOS 26+ so UIKit can preserve the\n * system Liquid Glass navigation appearance.\n */\n background?: string;\n\n /**\n * Title and label text color where the native platform supports it.\n */\n foreground?: string;\n\n /**\n * Badge background color for native tab badges.\n */\n badgeBackground?: string;\n\n /**\n * Badge text color for native tab badges.\n */\n badgeText?: string;\n\n /**\n * Active tab indicator color on Android.\n */\n indicator?: string;\n\n /**\n * Tab press ripple color on Android.\n */\n ripple?: string;\n}\n\n/**\n * Global plugin configuration.\n */\nexport interface NativeNavigationConfigureOptions {\n /**\n * Enables or disables the native chrome host.\n */\n enabled?: boolean;\n\n /**\n * Native style preference. `auto` uses the current platform.\n */\n platformStyle?: NativeNavigationPlatformStyle;\n\n /**\n * When `css`, the plugin writes CSS variables on `document.documentElement`.\n */\n contentInsetMode?: NativeNavigationContentInsetMode;\n\n /**\n * Default native transition duration in milliseconds.\n */\n animationDuration?: number;\n\n /**\n * Shared color hints for native bars.\n */\n colors?: NativeNavigationColors;\n}\n\n/**\n * A button shown in the native navbar.\n */\nexport interface NativeNavigationBarButton {\n /**\n * Stable id returned in `navbarItemTap`.\n */\n id: string;\n\n /**\n * Visible text label.\n */\n title?: string;\n\n /**\n * Native icon descriptor.\n */\n icon?: NativeNavigationIcon;\n\n /**\n * Whether the action is enabled. Defaults to `true`.\n */\n enabled?: boolean;\n}\n\n/**\n * Native back button configuration.\n */\nexport interface NativeNavigationBackButton {\n /**\n * Show the native back affordance.\n */\n visible?: boolean;\n\n /**\n * Optional back title.\n */\n title?: string;\n}\n\n/**\n * Native navbar state.\n */\nexport interface NativeNavigationNavbarOptions {\n /**\n * Hide the native navbar.\n */\n hidden?: boolean;\n\n /**\n * Main title.\n */\n title?: string;\n\n /**\n * Secondary title where supported by the platform.\n */\n subtitle?: string;\n\n /**\n * Prefer a large iOS title style.\n */\n large?: boolean;\n\n /**\n * Prefer transparent/scroll-edge style.\n */\n transparent?: boolean;\n\n /**\n * iOS blur/material effect for the navbar background when glass is not\n * available. Defaults to `systemChromeMaterial` for transparent bars.\n */\n blurEffect?: NativeNavigationBlurEffect;\n\n /**\n * Back button state.\n */\n backButton?: NativeNavigationBackButton;\n\n /**\n * Left-side action buttons.\n */\n leftItems?: NativeNavigationBarButton[];\n\n /**\n * Right-side action buttons.\n */\n rightItems?: NativeNavigationBarButton[];\n\n /**\n * Navbar color hints.\n */\n colors?: NativeNavigationColors;\n\n /**\n * Animate native navbar changes.\n */\n animated?: boolean;\n}\n\n/**\n * A native tab item.\n */\nexport interface NativeNavigationTab {\n /**\n * Stable tab id returned in `tabSelect`.\n */\n id: string;\n\n /**\n * Visible tab label.\n */\n title?: string;\n\n /**\n * Native icon descriptor.\n */\n icon?: NativeNavigationIcon;\n\n /**\n * Optional selected-state icon.\n */\n selectedIcon?: NativeNavigationIcon;\n\n /**\n * Optional badge. Numeric badges are supported on both platforms; text badge\n * support depends on platform capabilities.\n */\n badge?: string | number;\n\n /**\n * Whether the tab is enabled. Defaults to `true`.\n */\n enabled?: boolean;\n}\n\n/**\n * Native tabbar state.\n */\nexport interface NativeNavigationTabbarOptions {\n /**\n * Hide the native tabbar.\n */\n hidden?: boolean;\n\n /**\n * Tab definitions.\n */\n tabs?: NativeNavigationTab[];\n\n /**\n * Currently selected tab id.\n */\n selectedId?: string;\n\n /**\n * Show text labels. Defaults to `true`.\n */\n labels?: boolean;\n\n /**\n * Native label visibility mode. Overrides `labels` when provided.\n */\n labelVisibilityMode?: NativeNavigationTabLabelVisibilityMode;\n\n /**\n * Show icons. Defaults to `true`.\n */\n icons?: boolean;\n\n /**\n * Tabbar color hints.\n */\n colors?: NativeNavigationColors;\n\n /**\n * iOS blur/material effect for the tabbar background when glass is not\n * available.\n */\n blurEffect?: NativeNavigationBlurEffect;\n\n /**\n * Keep the iOS scroll-edge tabbar appearance from becoming transparent.\n * Mirrors Expo Router native tabs' `disableTransparentOnScrollEdge` option.\n * Defaults to `false`.\n */\n disableTransparentOnScrollEdge?: boolean;\n\n /**\n * Disable the Android active tab indicator.\n */\n disableIndicator?: boolean;\n\n /**\n * Active tab indicator color on Android. `colors.indicator` is also\n * supported.\n */\n indicatorColor?: string;\n\n /**\n * Tab press ripple color on Android. `colors.ripple` is also supported.\n */\n rippleColor?: string;\n\n /**\n * Badge background color. `colors.badgeBackground` is also supported.\n */\n badgeBackgroundColor?: string;\n\n /**\n * Badge text color. `colors.badgeText` is also supported.\n */\n badgeTextColor?: string;\n\n /**\n * Animate native tabbar changes.\n */\n animated?: boolean;\n}\n\n/**\n * Insets exposed to web content.\n */\nexport interface NativeNavigationInsets {\n top: number;\n right: number;\n bottom: number;\n left: number;\n navbarHeight: number;\n tabbarHeight: number;\n}\n\n/**\n * Returned by methods that may change safe content bounds.\n */\nexport interface NativeNavigationInsetsResult {\n insets: NativeNavigationInsets;\n}\n\n/**\n * Begin a native transition transaction before JS changes route content.\n */\nexport interface NativeNavigationBeginTransitionOptions {\n id?: string;\n direction?: NativeNavigationTransitionDirection;\n duration?: number;\n /**\n * Source rectangle for `zoom` transitions. Use viewport coordinates such as\n * those returned by `Element.getBoundingClientRect()`.\n */\n sourceRect?: NativeNavigationRect;\n /**\n * Destination rectangle for shared-element-style `zoom` transitions.\n */\n targetRect?: NativeNavigationRect;\n /**\n * Corner radius used while animating a `zoom` transition.\n */\n cornerRadius?: number;\n}\n\n/**\n * Finish a native transition transaction after JS has changed route content.\n */\nexport interface NativeNavigationFinishTransitionOptions {\n id?: string;\n direction?: NativeNavigationTransitionDirection;\n duration?: number;\n /**\n * Source rectangle for `zoom` transitions when no active source was recorded.\n */\n sourceRect?: NativeNavigationRect;\n /**\n * Destination rectangle for shared-element-style `zoom` transitions.\n */\n targetRect?: NativeNavigationRect;\n /**\n * Corner radius used while animating a `zoom` transition.\n */\n cornerRadius?: number;\n}\n\n/**\n * Native transition result.\n */\nexport interface NativeNavigationTransitionResult {\n id: string;\n direction: NativeNavigationTransitionDirection;\n duration: number;\n}\n\n/**\n * Plugin version payload.\n */\nexport interface PluginVersionResult {\n /**\n * Version identifier returned by the platform implementation.\n */\n version: string;\n}\n\nexport interface NativeNavigationBackEvent {\n source: 'navbar';\n}\n\nexport interface NativeNavigationBarItemTapEvent {\n id: string;\n title?: string;\n placement: 'left' | 'right';\n}\n\nexport interface NativeNavigationTabSelectEvent {\n id: string;\n index: number;\n title?: string;\n}\n\nexport interface NativeNavigationSafeAreaChangedEvent {\n insets: NativeNavigationInsets;\n}\n\nexport interface NativeNavigationTransitionEvent {\n id: string;\n direction: NativeNavigationTransitionDirection;\n duration: number;\n}\n\n/**\n * Framework-agnostic native navigation chrome API.\n */\nexport interface NativeNavigationPlugin {\n /**\n * Configure the native chrome host and content inset behavior.\n */\n configure(options?: NativeNavigationConfigureOptions): Promise<NativeNavigationInsetsResult>;\n\n /**\n * Render or update the native navbar.\n */\n setNavbar(options: NativeNavigationNavbarOptions): Promise<NativeNavigationInsetsResult>;\n\n /**\n * Render or update the native tabbar.\n */\n setTabbar(options: NativeNavigationTabbarOptions): Promise<NativeNavigationInsetsResult>;\n\n /**\n * Capture the current WebView and prepare a native transition.\n */\n beginTransition(options?: NativeNavigationBeginTransitionOptions): Promise<NativeNavigationTransitionResult>;\n\n /**\n * Animate from the captured WebView snapshot to the current live WebView.\n */\n finishTransition(options?: NativeNavigationFinishTransitionOptions): Promise<NativeNavigationTransitionResult>;\n\n /**\n * Returns the platform implementation version marker.\n */\n getPluginVersion(): Promise<PluginVersionResult>;\n\n addListener(\n eventName: 'navbarBack',\n listenerFunc: (event: NativeNavigationBackEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'navbarItemTap',\n listenerFunc: (event: NativeNavigationBarItemTapEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'tabSelect',\n listenerFunc: (event: NativeNavigationTabSelectEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'safeAreaChanged',\n listenerFunc: (event: NativeNavigationSafeAreaChangedEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'transitionStart',\n listenerFunc: (event: NativeNavigationTransitionEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'transitionEnd',\n listenerFunc: (event: NativeNavigationTransitionEvent) => void,\n ): Promise<PluginListenerHandle>;\n}\n"]}
Binary file
Binary file
Binary file
@@ -2,7 +2,6 @@
2
2
 
3
3
  import Foundation
4
4
  import Capacitor
5
- import ObjectiveC
6
5
  import UIKit
7
6
 
8
7
  private struct NativeNavigationTransitionContext {
@@ -24,7 +23,7 @@ private struct NativeNavigationZoomTransitionContext {
24
23
 
25
24
  // swiftlint:disable type_body_length
26
25
  @objc(NativeNavigationPlugin)
27
- public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarDelegate {
26
+ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarControllerDelegate, UITabBarDelegate {
28
27
  public let identifier = "NativeNavigationPlugin"
29
28
  public let jsName = "NativeNavigation"
30
29
  public let pluginMethods: [CAPPluginMethod] = [
@@ -43,6 +42,8 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarDelega
43
42
  private var tabContainer: UIView?
44
43
  private var tabEffectView: UIVisualEffectView?
45
44
  private var tabBar: UITabBar?
45
+ private var tabBarController: NativeNavigationTabController?
46
+ private var tabViewControllers: [UIViewController] = []
46
47
  private var navbarHeight: CGFloat = 44
47
48
  private var tabbarHeight: CGFloat = 64
48
49
  private let floatingTabbarHorizontalMargin: CGFloat = 24
@@ -57,6 +58,7 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarDelega
57
58
  private var navbarItemTitle: [String: String] = [:]
58
59
  private var tabIds: [String] = []
59
60
  private var tabTitles: [String] = []
61
+ private var suppressTabSelectEvent = false
60
62
  private var transitionSnapshot: UIView?
61
63
  private var activeTransitionId: String?
62
64
  private var activeTransitionDirection = "forward"
@@ -94,6 +96,7 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarDelega
94
96
  if !self.isEnabled {
95
97
  self.navContainer?.isHidden = true
96
98
  self.tabContainer?.isHidden = true
99
+ self.tabBarController?.view.isHidden = true
97
100
  self.tabBar?.isHidden = true
98
101
  }
99
102
 
@@ -168,6 +171,7 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarDelega
168
171
 
169
172
  guard !hidden else {
170
173
  self.tabContainer?.isHidden = true
174
+ self.tabBarController?.view.isHidden = true
171
175
  self.tabBar?.isHidden = true
172
176
  self.updateInsetsAndNotify()
173
177
  call.resolve(self.insetsResult())
@@ -188,15 +192,20 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarDelega
188
192
  icons: icons
189
193
  )
190
194
 
191
- tabBar.items = items
192
- if let selectedIndex = selectedIndex, selectedIndex < items.count {
193
- tabBar.selectedItem = items[selectedIndex]
194
- } else if tabBar.selectedItem == nil {
195
- tabBar.selectedItem = items.first
195
+ if self.usesSystemLiquidGlass {
196
+ self.applySystemTabBarItems(items, selectedIndex: selectedIndex, animated: call.getBool("animated", false))
197
+ } else {
198
+ tabBar.items = items
199
+ if let selectedIndex = selectedIndex, selectedIndex < items.count {
200
+ tabBar.selectedItem = items[selectedIndex]
201
+ } else if tabBar.selectedItem == nil {
202
+ tabBar.selectedItem = items.first
203
+ }
196
204
  }
197
205
 
198
206
  self.applyTabBarAppearance(tabBar: tabBar, options: call)
199
207
  self.tabContainer?.isHidden = false
208
+ self.tabBarController?.view.isHidden = false
200
209
  tabBar.isHidden = false
201
210
  self.layoutChrome()
202
211
  self.updateInsetsAndNotify()
@@ -423,7 +432,18 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarDelega
423
432
  }
424
433
 
425
434
  public func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
426
- let index = item.tag
435
+ notifyTabSelect(index: item.tag)
436
+ }
437
+
438
+ public func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
439
+ guard !suppressTabSelectEvent else {
440
+ return
441
+ }
442
+ let index = tabBarController.viewControllers?.firstIndex(of: viewController) ?? viewController.tabBarItem.tag
443
+ notifyTabSelect(index: index)
444
+ }
445
+
446
+ private func notifyTabSelect(index: Int) {
427
447
  guard index >= 0 && index < tabIds.count else {
428
448
  return
429
449
  }
@@ -475,6 +495,10 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarDelega
475
495
  }
476
496
 
477
497
  private func ensureTabBar() -> UITabBar {
498
+ if usesSystemLiquidGlass {
499
+ return ensureSystemTabBar()
500
+ }
501
+
478
502
  if let tabBar = tabBar {
479
503
  return tabBar
480
504
  }
@@ -485,39 +509,24 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarDelega
485
509
  container.autoresizingMask = [.flexibleWidth, .flexibleTopMargin]
486
510
  container.backgroundColor = .clear
487
511
 
488
- if usesSystemLiquidGlass {
489
- if let effect = liquidGlassEffect() {
490
- let effectView = UIVisualEffectView(effect: effect)
491
- effectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
492
- effectView.isUserInteractionEnabled = false
493
- effectView.backgroundColor = .clear
494
- container.addSubview(effectView)
495
- self.tabEffectView = effectView
496
- }
497
- } else {
498
- container.layer.shadowColor = UIColor.black.cgColor
499
- container.layer.shadowOpacity = 0.14
500
- container.layer.shadowRadius = 18
501
- container.layer.shadowOffset = CGSize(width: 0, height: 10)
512
+ container.layer.shadowColor = UIColor.black.cgColor
513
+ container.layer.shadowOpacity = 0.14
514
+ container.layer.shadowRadius = 18
515
+ container.layer.shadowOffset = CGSize(width: 0, height: 10)
502
516
 
503
- let effectView = UIVisualEffectView(effect: UIBlurEffect(style: .systemChromeMaterial))
504
- effectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
505
- effectView.isUserInteractionEnabled = false
506
- effectView.clipsToBounds = true
507
- container.addSubview(effectView)
508
- self.tabEffectView = effectView
509
- }
517
+ let effectView = UIVisualEffectView(effect: UIBlurEffect(style: .systemChromeMaterial))
518
+ effectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
519
+ effectView.isUserInteractionEnabled = false
520
+ effectView.clipsToBounds = true
521
+ container.addSubview(effectView)
522
+ self.tabEffectView = effectView
510
523
 
511
524
  let bar = UITabBar()
512
525
  bar.isTranslucent = true
513
526
  bar.backgroundColor = .clear
514
- if usesSystemLiquidGlass {
515
- bar.isOpaque = false
516
- } else {
517
- bar.backgroundImage = UIImage()
518
- bar.shadowImage = UIImage()
519
- bar.clipsToBounds = true
520
- }
527
+ bar.backgroundImage = UIImage()
528
+ bar.shadowImage = UIImage()
529
+ bar.clipsToBounds = true
521
530
  bar.delegate = self
522
531
  bar.autoresizingMask = [.flexibleWidth, .flexibleTopMargin]
523
532
  container.addSubview(bar)
@@ -527,6 +536,52 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarDelega
527
536
  return bar
528
537
  }
529
538
 
539
+ private func ensureSystemTabBar() -> UITabBar {
540
+ if let tabBarController = tabBarController {
541
+ return tabBarController.tabBar
542
+ }
543
+
544
+ let controller = NativeNavigationTabController()
545
+ controller.delegate = self
546
+ controller.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
547
+ controller.view.isHidden = !tabbarVisible
548
+
549
+ if let parent = bridge?.viewController {
550
+ parent.addChild(controller)
551
+ parent.view.addSubview(controller.view)
552
+ controller.didMove(toParent: parent)
553
+ }
554
+
555
+ self.tabBarController = controller
556
+ self.tabBar = controller.tabBar
557
+ return controller.tabBar
558
+ }
559
+
560
+ private func applySystemTabBarItems(_ items: [UITabBarItem], selectedIndex: Int?, animated: Bool) {
561
+ guard let tabBarController = tabBarController else {
562
+ return
563
+ }
564
+
565
+ let previousSelectedIndex = tabBarController.selectedIndex
566
+ let controllers = items.map { item -> UIViewController in
567
+ let controller = NativeNavigationTabContentController()
568
+ controller.tabBarItem = item
569
+ return controller
570
+ }
571
+ let shouldAnimate = animated && tabBarController.viewControllers?.count == controllers.count
572
+
573
+ suppressTabSelectEvent = true
574
+ tabBarController.setViewControllers(controllers, animated: shouldAnimate)
575
+ if !controllers.isEmpty {
576
+ let fallbackIndex = selectedIndex ?? previousSelectedIndex
577
+ let index = min(max(fallbackIndex, 0), controllers.count - 1)
578
+ tabBarController.selectedIndex = index
579
+ }
580
+ suppressTabSelectEvent = false
581
+
582
+ tabViewControllers = controllers
583
+ }
584
+
530
585
  private func makeBarButtonItems(_ rawItems: [[String: Any]], placement: String) -> [UIBarButtonItem] {
531
586
  return rawItems.map { rawItem in
532
587
  let id = rawItem["id"] as? String ?? UUID().uuidString
@@ -792,6 +847,30 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarDelega
792
847
  }
793
848
 
794
849
  private func applyTabBarAppearance(tabBar: UITabBar, options call: CAPPluginCall) {
850
+ if usesSystemLiquidGlass {
851
+ let standardAppearance = UITabBarAppearance()
852
+ configureSystemTabBarStandardBackground(standardAppearance)
853
+ applyTabBarColorOptions(standardAppearance, tabBar: tabBar, options: call)
854
+ applyTabBarBadgeOptions(standardAppearance, options: call)
855
+
856
+ let scrollEdgeAppearance = UITabBarAppearance()
857
+ configureSystemTabBarScrollEdgeBackground(scrollEdgeAppearance, options: call)
858
+ applyTabBarColorOptions(scrollEdgeAppearance, tabBar: tabBar, options: call)
859
+ applyTabBarBadgeOptions(scrollEdgeAppearance, options: call)
860
+
861
+ tabBar.standardAppearance = standardAppearance
862
+ if #available(iOS 15.0, *) {
863
+ tabBar.scrollEdgeAppearance = scrollEdgeAppearance
864
+ }
865
+ tabBar.items?.forEach { item in
866
+ item.standardAppearance = standardAppearance
867
+ if #available(iOS 15.0, *) {
868
+ item.scrollEdgeAppearance = scrollEdgeAppearance
869
+ }
870
+ }
871
+ return
872
+ }
873
+
795
874
  let appearance = UITabBarAppearance()
796
875
  configureTabBarBackground(appearance, options: call)
797
876
  applyTabBarColorOptions(appearance, tabBar: tabBar, options: call)
@@ -804,23 +883,27 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarDelega
804
883
  }
805
884
 
806
885
  private func configureTabBarBackground(_ appearance: UITabBarAppearance, options call: CAPPluginCall) {
807
- if usesSystemLiquidGlass {
886
+ appearance.configureWithDefaultBackground()
887
+ if let effect = blurEffect(from: call.getString("blurEffect"), fallback: nil) {
808
888
  appearance.configureWithTransparentBackground()
809
889
  appearance.backgroundColor = .clear
810
- appearance.backgroundEffect = nil
811
- appearance.shadowColor = .clear
812
- tabEffectView?.effect = liquidGlassEffect()
813
- tabEffectView?.isHidden = tabEffectView?.effect == nil
890
+ tabEffectView?.effect = effect
891
+ tabEffectView?.isHidden = false
814
892
  } else {
815
- appearance.configureWithDefaultBackground()
816
- if let effect = blurEffect(from: call.getString("blurEffect"), fallback: nil) {
817
- appearance.configureWithTransparentBackground()
818
- appearance.backgroundColor = .clear
819
- tabEffectView?.effect = effect
820
- tabEffectView?.isHidden = false
821
- } else {
822
- tabEffectView?.isHidden = true
823
- }
893
+ tabEffectView?.isHidden = true
894
+ }
895
+ }
896
+
897
+ private func configureSystemTabBarStandardBackground(_ appearance: UITabBarAppearance) {
898
+ appearance.configureWithDefaultBackground()
899
+ }
900
+
901
+ private func configureSystemTabBarScrollEdgeBackground(_ appearance: UITabBarAppearance, options call: CAPPluginCall) {
902
+ if call.getBool("disableTransparentOnScrollEdge", false) {
903
+ configureSystemTabBarStandardBackground(appearance)
904
+ } else {
905
+ appearance.configureWithTransparentBackground()
906
+ appearance.shadowColor = .clear
824
907
  }
825
908
  }
826
909
 
@@ -904,32 +987,23 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarDelega
904
987
  }
905
988
 
906
989
  if let container = tabContainer {
907
- if usesSystemLiquidGlass {
908
- container.frame = CGRect(
909
- x: 0,
910
- y: height - safeInsets.bottom - tabbarHeight,
911
- width: width,
912
- height: tabbarHeight + safeInsets.bottom
913
- )
914
- container.layer.cornerRadius = 0
915
- container.layer.shadowOpacity = 0
916
- container.layer.shadowPath = nil
917
- tabEffectView?.frame = container.bounds
918
- tabBar?.frame = container.bounds
919
- tabBar?.layer.cornerRadius = 0
920
- } else {
921
- let availableWidth = max(0, width - (floatingTabbarHorizontalMargin * 2))
922
- let tabbarWidth = min(availableWidth, floatingTabbarMaxWidth)
923
- let originX = (width - tabbarWidth) / 2
924
- let originY = height - safeInsets.bottom - floatingTabbarBottomGap - tabbarHeight
925
- container.frame = CGRect(x: originX, y: originY, width: tabbarWidth, height: tabbarHeight)
926
- container.layer.cornerRadius = tabbarHeight / 2
927
- container.layer.shadowPath = UIBezierPath(roundedRect: container.bounds, cornerRadius: tabbarHeight / 2).cgPath
928
- tabEffectView?.frame = container.bounds
929
- tabEffectView?.layer.cornerRadius = tabbarHeight / 2
930
- tabBar?.frame = container.bounds
931
- tabBar?.layer.cornerRadius = tabbarHeight / 2
932
- }
990
+ let availableWidth = max(0, width - (floatingTabbarHorizontalMargin * 2))
991
+ let tabbarWidth = min(availableWidth, floatingTabbarMaxWidth)
992
+ let originX = (width - tabbarWidth) / 2
993
+ let originY = height - safeInsets.bottom - floatingTabbarBottomGap - tabbarHeight
994
+ container.frame = CGRect(x: originX, y: originY, width: tabbarWidth, height: tabbarHeight)
995
+ container.layer.cornerRadius = tabbarHeight / 2
996
+ container.layer.shadowPath = UIBezierPath(roundedRect: container.bounds, cornerRadius: tabbarHeight / 2).cgPath
997
+ tabEffectView?.frame = container.bounds
998
+ tabEffectView?.layer.cornerRadius = tabbarHeight / 2
999
+ tabBar?.frame = container.bounds
1000
+ tabBar?.layer.cornerRadius = tabbarHeight / 2
1001
+ }
1002
+
1003
+ if let tabBarController = tabBarController {
1004
+ tabBarController.view.frame = CGRect(x: 0, y: 0, width: width, height: height)
1005
+ tabBarController.view.setNeedsLayout()
1006
+ tabBarController.view.layoutIfNeeded()
933
1007
  }
934
1008
 
935
1009
  bringChromeToFront()
@@ -942,6 +1016,9 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarDelega
942
1016
  if let tabContainer = tabContainer {
943
1017
  bridge?.viewController?.view.bringSubviewToFront(tabContainer)
944
1018
  }
1019
+ if let tabBarController = tabBarController {
1020
+ bridge?.viewController?.view.bringSubviewToFront(tabBarController.view)
1021
+ }
945
1022
  }
946
1023
 
947
1024
  private func colorValue(_ value: Any?) -> UIColor? {
@@ -973,23 +1050,6 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarDelega
973
1050
  return UIBlurEffect(style: style)
974
1051
  }
975
1052
 
976
- private func liquidGlassEffect() -> UIVisualEffect? {
977
- guard usesSystemLiquidGlass,
978
- let effectClass = NSClassFromString("UIGlassEffect") else {
979
- return nil
980
- }
981
-
982
- let selector = NSSelectorFromString("effectWithStyle:")
983
- guard let method = class_getClassMethod(effectClass, selector) else {
984
- return nil
985
- }
986
-
987
- typealias EffectWithStyle = @convention(c) (AnyClass, Selector, Int) -> AnyObject?
988
- let implementation = method_getImplementation(method)
989
- let factory = unsafeBitCast(implementation, to: EffectWithStyle.self)
990
- return factory(effectClass, selector, 0) as? UIVisualEffect
991
- }
992
-
993
1053
  private func blurStyle(from value: String?) -> UIBlurEffect.Style? {
994
1054
  guard let value = value else {
995
1055
  return nil
@@ -1040,9 +1100,11 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarDelega
1040
1100
 
1041
1101
  private func currentInsets() -> [String: Any] {
1042
1102
  let safeInsets = bridge?.viewController?.view.safeAreaInsets ?? .zero
1043
- let navHeight = navbarVisible ? navbarHeight + safeInsets.top : 0
1044
- let tabbarGap = usesSystemLiquidGlass ? 0 : floatingTabbarBottomGap
1045
- let tabHeight = tabbarVisible ? tabbarHeight + safeInsets.bottom + tabbarGap : 0
1103
+ let navHeight = isEnabled && navbarVisible ? navbarHeight + safeInsets.top : 0
1104
+ let nativeTabHeight = max(tabBar?.frame.height ?? 0, 49 + safeInsets.bottom)
1105
+ let tabHeight = isEnabled && tabbarVisible
1106
+ ? (usesSystemLiquidGlass ? nativeTabHeight : tabbarHeight + safeInsets.bottom + floatingTabbarBottomGap)
1107
+ : 0
1046
1108
  return [
1047
1109
  "top": navHeight,
1048
1110
  "right": safeInsets.right,
@@ -1170,6 +1232,44 @@ private final class NativeNavigationBar: UINavigationBar {
1170
1232
  }
1171
1233
  }
1172
1234
 
1235
+ private final class NativeNavigationTabController: UITabBarController {
1236
+ override func loadView() {
1237
+ let view = NativeNavigationTabControllerView()
1238
+ view.backgroundColor = .clear
1239
+ view.isOpaque = false
1240
+ self.view = view
1241
+ }
1242
+
1243
+ override func viewDidLoad() {
1244
+ super.viewDidLoad()
1245
+ view.backgroundColor = .clear
1246
+ view.isOpaque = false
1247
+ tabBar.isTranslucent = true
1248
+ (view as? NativeNavigationTabControllerView)?.tabBar = tabBar
1249
+ }
1250
+ }
1251
+
1252
+ private final class NativeNavigationTabControllerView: UIView {
1253
+ weak var tabBar: UITabBar?
1254
+
1255
+ override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
1256
+ guard let tabBar = tabBar, !tabBar.isHidden, tabBar.alpha > 0.01 else {
1257
+ return false
1258
+ }
1259
+ let tabPoint = convert(point, to: tabBar)
1260
+ return tabBar.point(inside: tabPoint, with: event)
1261
+ }
1262
+ }
1263
+
1264
+ private final class NativeNavigationTabContentController: UIViewController {
1265
+ override func loadView() {
1266
+ let view = UIView()
1267
+ view.backgroundColor = .clear
1268
+ view.isOpaque = false
1269
+ self.view = view
1270
+ }
1271
+ }
1272
+
1173
1273
  private final class SVGIconRenderer: NSObject, XMLParserDelegate {
1174
1274
  private let context: CGContext
1175
1275
  private var styleStack = [SVGRenderStyle()]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-native-navigation",
3
- "version": "8.0.10",
3
+ "version": "8.0.12",
4
4
  "description": "Capacitor plugin for native navbar, tabbar, and route transition chrome over a WebView.",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",