@capgo/capacitor-native-navigation 8.4.0 → 8.4.2

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.
@@ -3,7 +3,6 @@ ext {
3
3
  androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'
4
4
  androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0'
5
5
  androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'
6
- materialVersion = project.hasProperty('materialVersion') ? rootProject.ext.materialVersion : '1.12.0'
7
6
  }
8
7
 
9
8
  buildscript {
@@ -54,7 +53,6 @@ dependencies {
54
53
  implementation project(':capacitor-android')
55
54
  annotationProcessor project(':capacitor-android')
56
55
  implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
57
- implementation "com.google.android.material:material:$materialVersion"
58
56
  testImplementation "junit:junit:$junitVersion"
59
57
  androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
60
58
  androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
@@ -1883,18 +1883,29 @@ public class NativeNavigationPlugin extends Plugin {
1883
1883
  }
1884
1884
 
1885
1885
  private JSObject currentInsets() {
1886
+ // View layout uses device pixels; CSS variables must be density-independent
1887
+ // or a 3x screen writes `192px` which CSS treats as 192 CSS px.
1886
1888
  int top = navbarVisible ? statusBarInset() + dp(DEFAULT_NAVBAR_DP) : 0;
1887
1889
  int bottom = tabbarVisible ? navigationBarInset() + dp(tabbarStyle.totalHeight()) + dp(tabbarStyle.bottomGap) : 0;
1888
1890
  JSObject insets = new JSObject();
1889
- insets.put("top", top);
1891
+ insets.put("top", cssPx(top));
1890
1892
  insets.put("right", 0);
1891
- insets.put("bottom", bottom);
1893
+ insets.put("bottom", cssPx(bottom));
1892
1894
  insets.put("left", 0);
1893
- insets.put("navbarHeight", top);
1894
- insets.put("tabbarHeight", bottom);
1895
+ insets.put("navbarHeight", cssPx(top));
1896
+ insets.put("tabbarHeight", cssPx(bottom));
1895
1897
  return insets;
1896
1898
  }
1897
1899
 
1900
+ /** Converts a layout pixel value to CSS pixels for WebView CSS variables. */
1901
+ private int cssPx(int devicePx) {
1902
+ float density = getContext().getResources().getDisplayMetrics().density;
1903
+ if (density <= 0f) {
1904
+ return devicePx;
1905
+ }
1906
+ return Math.round(devicePx / density);
1907
+ }
1908
+
1898
1909
  private JSObject insetsResult() {
1899
1910
  JSObject result = new JSObject();
1900
1911
  result.put("insets", currentInsets());
@@ -69,6 +69,8 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
69
69
  private var tabBaseImages: [UIImage?] = []
70
70
  private var tabSelectedImages: [UIImage?] = []
71
71
  private var suppressTabSelectEvent = false
72
+ private var tabSelectEmittedForReTap = false
73
+ private var lastSetTabbarOptions: [String: Any] = [:]
72
74
  private var transitionSnapshot: UIView?
73
75
  private var activeTransitionId: String?
74
76
  private var activeTransitionDirection = "forward"
@@ -233,6 +235,9 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
233
235
  icons: icons
234
236
  )
235
237
  self.applySystemTabBarItems(items, selectedIndex: selectedIndex, animated: call.getBool("animated", false))
238
+ if let options = call.options as? [String: Any] {
239
+ self.lastSetTabbarOptions = options
240
+ }
236
241
  self.applyTabBarAppearance(tabBar: tabBar, options: call)
237
242
  if items.isEmpty {
238
243
  self.tabbarVisible = false
@@ -543,17 +548,27 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
543
548
  if usesSystemLiquidGlass {
544
549
  hostWebView(in: viewController)
545
550
  }
551
+ tabSelectEmittedForReTap = false
552
+ if !suppressTabSelectEvent,
553
+ tabBarController.selectedViewController === viewController {
554
+ notifyTabSelect(index: viewController.tabBarItem.tag)
555
+ tabSelectEmittedForReTap = true
556
+ }
546
557
  return true
547
558
  }
548
559
 
549
560
  public func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
550
561
  guard !suppressTabSelectEvent else {
562
+ tabSelectEmittedForReTap = false
551
563
  hostWebViewInSelectedSystemTab()
552
564
  return
553
565
  }
554
566
  let index = viewController.tabBarItem.tag
555
567
  hostWebViewInSelectedSystemTab()
556
- notifyTabSelect(index: index)
568
+ if !tabSelectEmittedForReTap {
569
+ notifyTabSelect(index: index)
570
+ }
571
+ tabSelectEmittedForReTap = false
557
572
  }
558
573
 
559
574
  private func notifyTabSelect(index: Int) {
@@ -611,17 +626,29 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
611
626
  tabBarController?.view.layoutIfNeeded()
612
627
 
613
628
  if let tabBar = tabBar {
614
- tabBar.isTranslucent = !prefersOpaqueTabBarBackground()
615
- let standardAppearance = tabBar.standardAppearance
616
- tabBar.standardAppearance = standardAppearance
617
- if #available(iOS 15.0, *) {
618
- let scrollEdgeAppearance = tabBar.scrollEdgeAppearance ?? standardAppearance
619
- tabBar.scrollEdgeAppearance = scrollEdgeAppearance
620
- }
621
- tabBar.items?.forEach { item in
622
- item.standardAppearance = standardAppearance
629
+ if usesSystemLiquidGlass,
630
+ !lastSetTabbarOptions.isEmpty,
631
+ let refreshCall = CAPPluginCall(
632
+ callbackId: "tabbar-refresh",
633
+ methodName: "setTabbar",
634
+ options: lastSetTabbarOptions,
635
+ success: { _, _ in },
636
+ error: { _ in }
637
+ ) {
638
+ applyTabBarAppearance(tabBar: tabBar, options: refreshCall)
639
+ } else {
640
+ tabBar.isTranslucent = !prefersOpaqueTabBarBackground()
641
+ let standardAppearance = tabBar.standardAppearance
642
+ tabBar.standardAppearance = standardAppearance
623
643
  if #available(iOS 15.0, *) {
624
- item.scrollEdgeAppearance = tabBar.scrollEdgeAppearance
644
+ let scrollEdgeAppearance = tabBar.scrollEdgeAppearance ?? standardAppearance
645
+ tabBar.scrollEdgeAppearance = scrollEdgeAppearance
646
+ }
647
+ tabBar.items?.forEach { item in
648
+ item.standardAppearance = standardAppearance
649
+ if #available(iOS 15.0, *) {
650
+ item.scrollEdgeAppearance = tabBar.scrollEdgeAppearance
651
+ }
625
652
  }
626
653
  }
627
654
  tabBar.setNeedsLayout()
@@ -1469,7 +1496,9 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
1469
1496
  private func applyTabBarAppearance(tabBar: UITabBar, options call: CAPPluginCall) {
1470
1497
  if usesSystemLiquidGlass {
1471
1498
  let standardAppearance = UITabBarAppearance()
1472
- configureSystemTabBarStandardBackground(standardAppearance)
1499
+ // Match scroll-edge background so UIKit does not swap Liquid Glass for
1500
+ // legacy material when content scrolls under the bar (iOS 26+).
1501
+ configureSystemTabBarScrollEdgeBackground(standardAppearance, options: call)
1473
1502
  applyTabBarColorOptions(standardAppearance, tabBar: tabBar, options: call)
1474
1503
  applyTabBarBadgeOptions(standardAppearance, options: call)
1475
1504
 
@@ -1791,7 +1820,13 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
1791
1820
 
1792
1821
  if let container = systemTabRootContainer {
1793
1822
  container.frame = rootView.bounds
1794
- webView?.frame = container.bounds
1823
+ // Capacitor's root view is the WKWebView. After the webview is
1824
+ // hosted in a tab content controller, stretching it to this
1825
+ // container's full bounds overflows the tab VC (clipsToBounds is
1826
+ // off) and paints over the system tab bar.
1827
+ if !isWebViewHostedInSystemTabController {
1828
+ webView?.frame = container.bounds
1829
+ }
1795
1830
  }
1796
1831
 
1797
1832
  if let container = navContainer {
@@ -1852,8 +1887,11 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
1852
1887
 
1853
1888
  private func bringChromeToFront() {
1854
1889
  if usesSystemLiquidGlass && tabbarStyle.shape != .curve {
1890
+ if let tabBarController = tabBarController {
1891
+ (systemTabRootContainer ?? bridge?.viewController?.view)?.bringSubviewToFront(tabBarController.view)
1892
+ }
1855
1893
  if let navContainer = navContainer {
1856
- bridge?.viewController?.view.bringSubviewToFront(navContainer)
1894
+ (systemTabRootContainer ?? bridge?.viewController?.view)?.bringSubviewToFront(navContainer)
1857
1895
  }
1858
1896
  bringLiftedWebViewOverlaysToFront()
1859
1897
  return
@@ -1975,7 +2013,12 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
1975
2013
  let usesSystemTabbar = usesSystemLiquidGlass && tabbarStyle.shape != .curve
1976
2014
  let nativeTabHeight = max(tabBar?.frame.height ?? 0, 49 + safeInsets.bottom)
1977
2015
  let customTabHeight = tabbarHeight + safeInsets.bottom + tabbarStyle.bottomGap
1978
- let tabHeight = isEnabled && tabbarVisible ? (usesSystemTabbar ? nativeTabHeight : customTabHeight) : 0
2016
+ // Hosted in the tab content VC: the WebView already ends above the
2017
+ // bar. Reporting the bar height as CSS overlap lifts the FAB by a
2018
+ // second bar.
2019
+ let tabHeight = isEnabled && tabbarVisible && !isWebViewHostedInSystemTabController
2020
+ ? (usesSystemTabbar ? nativeTabHeight : customTabHeight)
2021
+ : 0
1979
2022
  return [
1980
2023
  "top": navHeight,
1981
2024
  "right": safeInsets.right,
@@ -2674,6 +2717,7 @@ final class NativeNavigationTabContentController: UIViewController {
2674
2717
  let view = UIView()
2675
2718
  view.backgroundColor = .systemBackground
2676
2719
  view.isOpaque = true
2720
+ view.clipsToBounds = true
2677
2721
  self.view = view
2678
2722
  }
2679
2723
 
@@ -2714,7 +2758,8 @@ final class NativeNavigationTabContentController: UIViewController {
2714
2758
  snapshotPlaceholder = nil
2715
2759
  hostedWebView = webView
2716
2760
  if webView.superview !== view {
2717
- webView.removeFromSuperview()
2761
+ // addSubview reparents in one step. removeFromSuperview() first
2762
+ // leaves a frame of this VC's backing colour (system white).
2718
2763
  view.addSubview(webView)
2719
2764
  }
2720
2765
  webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-native-navigation",
3
- "version": "8.4.0",
3
+ "version": "8.4.2",
4
4
  "description": "Capacitor plugin for native navbar, tabbar, safe-area handling, and WebView snapshot transitions.",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",