@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 +19 -18
- package/dist/docs.json +7 -0
- package/dist/esm/definitions.d.ts +6 -0
- package/dist/esm/definitions.js.map +1 -1
- package/docs/demo-navigation.webp +0 -0
- package/docs/demo-options.webp +0 -0
- package/docs/demo-svg-icons.webp +0 -0
- package/ios/Sources/NativeNavigationPlugin/NativeNavigationPlugin.swift +196 -96
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# @capgo/capacitor-native-navigation
|
|
2
|
-
<a href="https://capgo.app/"><img src="https://
|
|
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+
|
|
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
|
|
682
|
-
|
|
|
683
|
-
| **`hidden`**
|
|
684
|
-
| **`tabs`**
|
|
685
|
-
| **`selectedId`**
|
|
686
|
-
| **`labels`**
|
|
687
|
-
| **`labelVisibilityMode`**
|
|
688
|
-
| **`icons`**
|
|
689
|
-
| **`colors`**
|
|
690
|
-
| **`blurEffect`**
|
|
691
|
-
| **`
|
|
692
|
-
| **`
|
|
693
|
-
| **`
|
|
694
|
-
| **`
|
|
695
|
-
| **`
|
|
696
|
-
| **`
|
|
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
|
package/docs/demo-options.webp
CHANGED
|
Binary file
|
package/docs/demo-svg-icons.webp
CHANGED
|
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
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
-
|
|
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
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
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
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
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
|
-
|
|
515
|
-
|
|
516
|
-
|
|
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
|
-
|
|
886
|
+
appearance.configureWithDefaultBackground()
|
|
887
|
+
if let effect = blurEffect(from: call.getString("blurEffect"), fallback: nil) {
|
|
808
888
|
appearance.configureWithTransparentBackground()
|
|
809
889
|
appearance.backgroundColor = .clear
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
tabEffectView?.effect = liquidGlassEffect()
|
|
813
|
-
tabEffectView?.isHidden = tabEffectView?.effect == nil
|
|
890
|
+
tabEffectView?.effect = effect
|
|
891
|
+
tabEffectView?.isHidden = false
|
|
814
892
|
} else {
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
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
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
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
|
|
1045
|
-
let tabHeight =
|
|
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.
|
|
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",
|