@capgo/capacitor-native-navigation 8.0.11 → 8.0.13

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>
@@ -44,6 +44,10 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
44
44
  private var tabBar: UITabBar?
45
45
  private var tabBarController: NativeNavigationTabController?
46
46
  private var tabViewControllers: [UIViewController] = []
47
+ private weak var originalWebViewSuperview: UIView?
48
+ private var originalWebViewIndex: Int?
49
+ private var originalWebViewAutoresizingMask: UIView.AutoresizingMask?
50
+ private var isWebViewHostedInSystemTabController = false
47
51
  private var navbarHeight: CGFloat = 44
48
52
  private var tabbarHeight: CGFloat = 64
49
53
  private let floatingTabbarHorizontalMargin: CGFloat = 24
@@ -96,6 +100,7 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
96
100
  if !self.isEnabled {
97
101
  self.navContainer?.isHidden = true
98
102
  self.tabContainer?.isHidden = true
103
+ self.restoreWebViewFromSystemTabController()
99
104
  self.tabBarController?.view.isHidden = true
100
105
  self.tabBar?.isHidden = true
101
106
  }
@@ -170,9 +175,7 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
170
175
  self.tabbarVisible = !hidden
171
176
 
172
177
  guard !hidden else {
173
- self.tabContainer?.isHidden = true
174
- self.tabBarController?.view.isHidden = true
175
- self.tabBar?.isHidden = true
178
+ self.hideTabBarChrome()
176
179
  self.updateInsetsAndNotify()
177
180
  call.resolve(self.insetsResult())
178
181
  return
@@ -204,9 +207,7 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
204
207
  }
205
208
 
206
209
  self.applyTabBarAppearance(tabBar: tabBar, options: call)
207
- self.tabContainer?.isHidden = false
208
- self.tabBarController?.view.isHidden = false
209
- tabBar.isHidden = false
210
+ self.showTabBarChrome(tabBar)
210
211
  self.layoutChrome()
211
212
  self.updateInsetsAndNotify()
212
213
  call.resolve(self.insetsResult())
@@ -215,7 +216,8 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
215
216
 
216
217
  @objc func beginTransition(_ call: CAPPluginCall) {
217
218
  DispatchQueue.main.async {
218
- guard let webView = self.webView, let rootView = self.bridge?.viewController?.view else {
219
+ guard let webView = self.webView,
220
+ let transitionContainer = webView.superview else {
219
221
  call.reject("WebView unavailable")
220
222
  return
221
223
  }
@@ -224,7 +226,7 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
224
226
  let direction = call.getString("direction") ?? "forward"
225
227
  let durationMs = Int((call.getDouble("duration") ?? self.defaultTransitionDuration * 1_000).rounded())
226
228
  let zoomSourceRect = direction == "zoom" ? self.transitionRect(call.getObject("sourceRect")) : nil
227
- let zoomSourceFrame = zoomSourceRect.map { self.rootFrame(for: $0, webView: webView) }
229
+ let zoomSourceFrame = zoomSourceRect.map { self.transitionFrame(for: $0, webView: webView) }
228
230
  let cornerRadius = CGFloat(call.getDouble("cornerRadius") ?? 0)
229
231
 
230
232
  self.transitionSnapshot?.removeFromSuperview()
@@ -233,7 +235,7 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
233
235
  snapshot.autoresizingMask = zoomSourceFrame == nil ? [.flexibleWidth, .flexibleHeight] : []
234
236
  snapshot.layer.cornerRadius = cornerRadius
235
237
  snapshot.clipsToBounds = cornerRadius > 0
236
- rootView.insertSubview(snapshot, aboveSubview: webView)
238
+ transitionContainer.insertSubview(snapshot, aboveSubview: webView)
237
239
  self.bringChromeToFront()
238
240
  self.transitionSnapshot = snapshot
239
241
  self.activeTransitionId = transitionId
@@ -274,8 +276,8 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
274
276
  let targetRect = self.transitionRect(call.getObject("targetRect"))
275
277
  self.finishZoomTransition(NativeNavigationZoomTransitionContext(
276
278
  transition: transition,
277
- sourceFrame: sourceRect.map { self.rootFrame(for: $0, webView: webView) },
278
- targetFrame: targetRect.map { self.rootFrame(for: $0, webView: webView) },
279
+ sourceFrame: sourceRect.map { self.transitionFrame(for: $0, webView: webView) },
280
+ targetFrame: targetRect.map { self.transitionFrame(for: $0, webView: webView) },
279
281
  cornerRadius: CGFloat(call.getDouble("cornerRadius") ?? Double(self.activeZoomCornerRadius))
280
282
  ))
281
283
  return
@@ -437,9 +439,11 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
437
439
 
438
440
  public func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
439
441
  guard !suppressTabSelectEvent else {
442
+ hostWebViewInSelectedSystemTab()
440
443
  return
441
444
  }
442
445
  let index = tabBarController.viewControllers?.firstIndex(of: viewController) ?? viewController.tabBarItem.tag
446
+ hostWebViewInSelectedSystemTab()
443
447
  notifyTabSelect(index: index)
444
448
  }
445
449
 
@@ -538,6 +542,7 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
538
542
 
539
543
  private func ensureSystemTabBar() -> UITabBar {
540
544
  if let tabBarController = tabBarController {
545
+ hostWebViewInSelectedSystemTab()
541
546
  return tabBarController.tabBar
542
547
  }
543
548
 
@@ -554,6 +559,7 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
554
559
 
555
560
  self.tabBarController = controller
556
561
  self.tabBar = controller.tabBar
562
+ hostWebViewInSelectedSystemTab()
557
563
  return controller.tabBar
558
564
  }
559
565
 
@@ -577,11 +583,93 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
577
583
  let index = min(max(fallbackIndex, 0), controllers.count - 1)
578
584
  tabBarController.selectedIndex = index
579
585
  }
586
+ hostWebViewInSelectedSystemTab()
580
587
  suppressTabSelectEvent = false
581
588
 
582
589
  tabViewControllers = controllers
583
590
  }
584
591
 
592
+ private func setSystemTabBarHidden(_ hidden: Bool) {
593
+ guard let tabBarController = tabBarController else {
594
+ return
595
+ }
596
+
597
+ if #available(iOS 18.0, *) {
598
+ tabBarController.setTabBarHidden(hidden, animated: false)
599
+ } else {
600
+ tabBarController.tabBar.isHidden = hidden
601
+ }
602
+ }
603
+
604
+ private func hideTabBarChrome() {
605
+ if usesSystemLiquidGlass {
606
+ setSystemTabBarHidden(true)
607
+ tabBarController?.view.isHidden = false
608
+ hostWebViewInSelectedSystemTab()
609
+ } else {
610
+ tabContainer?.isHidden = true
611
+ tabBar?.isHidden = true
612
+ }
613
+ }
614
+
615
+ private func showTabBarChrome(_ tabBar: UITabBar) {
616
+ tabContainer?.isHidden = false
617
+ tabBarController?.view.isHidden = false
618
+ if usesSystemLiquidGlass {
619
+ setSystemTabBarHidden(false)
620
+ hostWebViewInSelectedSystemTab()
621
+ } else {
622
+ tabBar.isHidden = false
623
+ }
624
+ }
625
+
626
+ private func captureOriginalWebViewPlacementIfNeeded(_ webView: UIView) {
627
+ guard originalWebViewSuperview == nil, let superview = webView.superview else {
628
+ return
629
+ }
630
+
631
+ originalWebViewSuperview = superview
632
+ originalWebViewIndex = superview.subviews.firstIndex(of: webView)
633
+ originalWebViewAutoresizingMask = webView.autoresizingMask
634
+ }
635
+
636
+ private func hostWebViewInSelectedSystemTab() {
637
+ guard usesSystemLiquidGlass,
638
+ let webView = webView,
639
+ let selectedController = tabBarController?.selectedViewController as? NativeNavigationTabContentController else {
640
+ return
641
+ }
642
+
643
+ captureOriginalWebViewPlacementIfNeeded(webView)
644
+ clearHostedWebViews(matching: webView, except: selectedController)
645
+ selectedController.host(webView: webView)
646
+ clearHostedWebViews(matching: webView, except: selectedController)
647
+ isWebViewHostedInSystemTabController = true
648
+ }
649
+
650
+ private func restoreWebViewFromSystemTabController() {
651
+ guard isWebViewHostedInSystemTabController,
652
+ let webView = webView,
653
+ let targetSuperview = originalWebViewSuperview ?? bridge?.viewController?.view else {
654
+ return
655
+ }
656
+
657
+ let insertionIndex = min(originalWebViewIndex ?? targetSuperview.subviews.count, targetSuperview.subviews.count)
658
+ clearHostedWebViews(matching: webView)
659
+ webView.removeFromSuperview()
660
+ targetSuperview.insertSubview(webView, at: insertionIndex)
661
+ webView.autoresizingMask = originalWebViewAutoresizingMask ?? [.flexibleWidth, .flexibleHeight]
662
+ webView.frame = targetSuperview.bounds
663
+ isWebViewHostedInSystemTabController = false
664
+ }
665
+
666
+ private func clearHostedWebViews(matching webView: UIView, except owner: NativeNavigationTabContentController? = nil) {
667
+ tabViewControllers
668
+ .compactMap { $0 as? NativeNavigationTabContentController }
669
+ .filter { $0 !== owner }
670
+ .forEach { $0.clearHostedWebView(ifMatching: webView) }
671
+ }
672
+
585
673
  private func makeBarButtonItems(_ rawItems: [[String: Any]], placement: String) -> [UIBarButtonItem] {
586
674
  return rawItems.map { rawItem in
587
675
  let id = rawItem["id"] as? String ?? UUID().uuidString
@@ -761,13 +849,16 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
761
849
  )
762
850
  }
763
851
 
764
- private func rootFrame(for viewportRect: CGRect, webView: UIView) -> CGRect {
765
- return CGRect(
766
- x: webView.frame.minX + viewportRect.minX,
767
- y: webView.frame.minY + viewportRect.minY,
768
- width: viewportRect.width,
769
- height: viewportRect.height
770
- )
852
+ private func transitionFrame(for viewportRect: CGRect, webView: UIView) -> CGRect {
853
+ guard let transitionContainer = webView.superview else {
854
+ return CGRect(
855
+ x: webView.frame.minX + viewportRect.minX,
856
+ y: webView.frame.minY + viewportRect.minY,
857
+ width: viewportRect.width,
858
+ height: viewportRect.height
859
+ )
860
+ }
861
+ return webView.convert(viewportRect, to: transitionContainer)
771
862
  }
772
863
 
773
864
  private func transitionSnapshotView(from webView: UIView, sourceRect: CGRect?) -> UIView {
@@ -1010,6 +1101,13 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
1010
1101
  }
1011
1102
 
1012
1103
  private func bringChromeToFront() {
1104
+ if usesSystemLiquidGlass {
1105
+ if let navContainer = navContainer {
1106
+ bridge?.viewController?.view.bringSubviewToFront(navContainer)
1107
+ }
1108
+ return
1109
+ }
1110
+
1013
1111
  if let navContainer = navContainer {
1014
1112
  bridge?.viewController?.view.bringSubviewToFront(navContainer)
1015
1113
  }
@@ -1233,41 +1331,51 @@ private final class NativeNavigationBar: UINavigationBar {
1233
1331
  }
1234
1332
 
1235
1333
  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
1334
  override func viewDidLoad() {
1244
1335
  super.viewDidLoad()
1245
1336
  view.backgroundColor = .clear
1246
1337
  view.isOpaque = false
1247
1338
  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
1339
  }
1262
1340
  }
1263
1341
 
1264
1342
  private final class NativeNavigationTabContentController: UIViewController {
1343
+ private weak var hostedWebView: UIView?
1344
+
1265
1345
  override func loadView() {
1266
1346
  let view = UIView()
1267
1347
  view.backgroundColor = .clear
1268
1348
  view.isOpaque = false
1269
1349
  self.view = view
1270
1350
  }
1351
+
1352
+ override func viewDidLayoutSubviews() {
1353
+ super.viewDidLayoutSubviews()
1354
+ guard hostedWebView?.superview === view else {
1355
+ hostedWebView = nil
1356
+ return
1357
+ }
1358
+ hostedWebView?.frame = view.bounds
1359
+ }
1360
+
1361
+ func clearHostedWebView(ifMatching webView: UIView? = nil) {
1362
+ guard webView == nil || hostedWebView === webView else {
1363
+ return
1364
+ }
1365
+ hostedWebView = nil
1366
+ }
1367
+
1368
+ func host(webView: UIView) {
1369
+ if hostedWebView !== webView {
1370
+ hostedWebView = webView
1371
+ }
1372
+ if webView.superview !== view {
1373
+ webView.removeFromSuperview()
1374
+ view.addSubview(webView)
1375
+ }
1376
+ webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
1377
+ webView.frame = view.bounds
1378
+ }
1271
1379
  }
1272
1380
 
1273
1381
  private final class SVGIconRenderer: NSObject, XMLParserDelegate {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-native-navigation",
3
- "version": "8.0.11",
3
+ "version": "8.0.13",
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",