@capgo/capacitor-native-navigation 8.0.13 → 8.0.15-beta.pr12.18.1

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,7 @@ require 'json'
3
3
  package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
4
4
 
5
5
  Pod::Spec.new do |s|
6
- s.name = 'CapgoNativeNavigation'
6
+ s.name = 'CapgoCapacitorNativeNavigation'
7
7
  s.version = package['version']
8
8
  s.summary = package['description']
9
9
  s.license = package['license']
package/Package.swift CHANGED
@@ -2,11 +2,11 @@
2
2
  import PackageDescription
3
3
 
4
4
  let package = Package(
5
- name: "CapgoNativeNavigation",
5
+ name: "CapgoCapacitorNativeNavigation",
6
6
  platforms: [.iOS(.v15)],
7
7
  products: [
8
8
  .library(
9
- name: "CapgoNativeNavigation",
9
+ name: "CapgoCapacitorNativeNavigation",
10
10
  targets: ["NativeNavigationPlugin"])
11
11
  ],
12
12
  dependencies: [
@@ -44,6 +44,7 @@ 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 systemTabRootContainer: UIView?
47
48
  private weak var originalWebViewSuperview: UIView?
48
49
  private var originalWebViewIndex: Int?
49
50
  private var originalWebViewAutoresizingMask: UIView.AutoresizingMask?
@@ -552,8 +553,9 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
552
553
  controller.view.isHidden = !tabbarVisible
553
554
 
554
555
  if let parent = bridge?.viewController {
556
+ let containerView = systemTabHostingContainerView(in: parent)
555
557
  parent.addChild(controller)
556
- parent.view.addSubview(controller.view)
558
+ insertSystemTabControllerView(controller.view, in: containerView)
557
559
  controller.didMove(toParent: parent)
558
560
  }
559
561
 
@@ -563,6 +565,49 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
563
565
  return controller.tabBar
564
566
  }
565
567
 
568
+ private func systemTabHostingContainerView(in parent: UIViewController) -> UIView {
569
+ if let systemTabRootContainer = systemTabRootContainer {
570
+ return systemTabRootContainer
571
+ }
572
+
573
+ guard let webView = webView,
574
+ parent.view === webView else {
575
+ return parent.view
576
+ }
577
+
578
+ let previousSuperview = webView.superview
579
+ let previousIndex = previousSuperview?.subviews.firstIndex(of: webView)
580
+ let previousFrame = webView.frame
581
+ let previousAutoresizingMask = webView.autoresizingMask
582
+ let container = UIView(frame: previousFrame)
583
+ container.backgroundColor = .clear
584
+ container.isOpaque = false
585
+ container.autoresizingMask = previousAutoresizingMask.isEmpty ? [.flexibleWidth, .flexibleHeight] : previousAutoresizingMask
586
+
587
+ if let previousSuperview = previousSuperview {
588
+ previousSuperview.insertSubview(container, at: min(previousIndex ?? previousSuperview.subviews.count, previousSuperview.subviews.count))
589
+ }
590
+
591
+ parent.view = container
592
+ container.addSubview(webView)
593
+ webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
594
+ webView.frame = container.bounds
595
+ moveNativeChrome(from: webView, to: container)
596
+
597
+ systemTabRootContainer = container
598
+ originalWebViewSuperview = container
599
+ originalWebViewIndex = 0
600
+ originalWebViewAutoresizingMask = webView.autoresizingMask
601
+ return container
602
+ }
603
+
604
+ private func moveNativeChrome(from webView: UIView, to container: UIView) {
605
+ if let navContainer = navContainer,
606
+ navContainer.superview === webView {
607
+ container.addSubview(navContainer)
608
+ }
609
+ }
610
+
566
611
  private func applySystemTabBarItems(_ items: [UITabBarItem], selectedIndex: Int?, animated: Bool) {
567
612
  guard let tabBarController = tabBarController else {
568
613
  return
@@ -633,6 +678,43 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
633
678
  originalWebViewAutoresizingMask = webView.autoresizingMask
634
679
  }
635
680
 
681
+ private func insertSystemTabControllerView(_ controllerView: UIView, in parentView: UIView) {
682
+ guard let webView = webView else {
683
+ parentView.addSubview(controllerView)
684
+ return
685
+ }
686
+
687
+ captureOriginalWebViewPlacementIfNeeded(webView)
688
+ let insertionIndex = systemTabControllerInsertionIndex(in: parentView, for: webView)
689
+ parentView.insertSubview(controllerView, at: insertionIndex)
690
+ }
691
+
692
+ private func systemTabControllerInsertionIndex(in parentView: UIView, for webView: UIView) -> Int {
693
+ if let directChild = directChild(of: parentView, containing: webView),
694
+ let index = parentView.subviews.firstIndex(of: directChild) {
695
+ return min(index, parentView.subviews.count)
696
+ }
697
+
698
+ if let originalWebViewSuperview = originalWebViewSuperview,
699
+ originalWebViewSuperview === parentView {
700
+ return min(originalWebViewIndex ?? parentView.subviews.count, parentView.subviews.count)
701
+ }
702
+
703
+ return parentView.subviews.count
704
+ }
705
+
706
+ private func directChild(of ancestor: UIView, containing descendant: UIView) -> UIView? {
707
+ var current: UIView? = descendant
708
+ while let view = current, let superview = view.superview {
709
+ if superview === ancestor {
710
+ return view
711
+ }
712
+ current = superview
713
+ }
714
+
715
+ return nil
716
+ }
717
+
636
718
  private func hostWebViewInSelectedSystemTab() {
637
719
  guard usesSystemLiquidGlass,
638
720
  let webView = webView,
@@ -642,7 +724,10 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
642
724
 
643
725
  captureOriginalWebViewPlacementIfNeeded(webView)
644
726
  clearHostedWebViews(matching: webView, except: selectedController)
645
- selectedController.host(webView: webView)
727
+ guard selectedController.host(webView: webView) else {
728
+ isWebViewHostedInSystemTabController = false
729
+ return
730
+ }
646
731
  clearHostedWebViews(matching: webView, except: selectedController)
647
732
  isWebViewHostedInSystemTabController = true
648
733
  }
@@ -1330,7 +1415,7 @@ private final class NativeNavigationBar: UINavigationBar {
1330
1415
  }
1331
1416
  }
1332
1417
 
1333
- private final class NativeNavigationTabController: UITabBarController {
1418
+ final class NativeNavigationTabController: UITabBarController {
1334
1419
  override func viewDidLoad() {
1335
1420
  super.viewDidLoad()
1336
1421
  view.backgroundColor = .clear
@@ -1339,7 +1424,7 @@ private final class NativeNavigationTabController: UITabBarController {
1339
1424
  }
1340
1425
  }
1341
1426
 
1342
- private final class NativeNavigationTabContentController: UIViewController {
1427
+ final class NativeNavigationTabContentController: UIViewController {
1343
1428
  private weak var hostedWebView: UIView?
1344
1429
 
1345
1430
  override func loadView() {
@@ -1365,16 +1450,21 @@ private final class NativeNavigationTabContentController: UIViewController {
1365
1450
  hostedWebView = nil
1366
1451
  }
1367
1452
 
1368
- func host(webView: UIView) {
1369
- if hostedWebView !== webView {
1370
- hostedWebView = webView
1453
+ @discardableResult
1454
+ func host(webView: UIView) -> Bool {
1455
+ guard view !== webView, !view.isDescendant(of: webView) else {
1456
+ hostedWebView = nil
1457
+ return false
1371
1458
  }
1459
+
1460
+ hostedWebView = webView
1372
1461
  if webView.superview !== view {
1373
1462
  webView.removeFromSuperview()
1374
1463
  view.addSubview(webView)
1375
1464
  }
1376
1465
  webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
1377
1466
  webView.frame = view.bounds
1467
+ return true
1378
1468
  }
1379
1469
  }
1380
1470
 
@@ -1,19 +1,37 @@
1
1
  import XCTest
2
+ import UIKit
2
3
  @testable import NativeNavigationPlugin
3
4
 
4
5
  class NativeNavigationTests: XCTestCase {
5
- func testEcho() {
6
- let implementation = NativeNavigation()
7
- let value = "Hello, World!"
8
- let result = implementation.echo(value)
9
-
10
- XCTAssertEqual(value, result)
11
- }
12
-
13
6
  func testGetPluginVersion() {
14
7
  let implementation = NativeNavigation()
15
8
  let result = implementation.getPluginVersion()
16
9
 
17
10
  XCTAssertEqual("native", result)
18
11
  }
12
+
13
+ func testTabContentControllerHostsWebView() {
14
+ let webView = UIView()
15
+ let originalContainer = UIView()
16
+ let controller = NativeNavigationTabContentController()
17
+ _ = controller.view
18
+
19
+ originalContainer.addSubview(webView)
20
+
21
+ XCTAssertTrue(controller.host(webView: webView))
22
+ XCTAssertEqual(webView.superview, controller.view)
23
+ XCTAssertEqual(webView.frame, controller.view.bounds)
24
+ }
25
+
26
+ func testTabContentControllerRejectsLayerCycle() {
27
+ let webView = UIView()
28
+ let controller = NativeNavigationTabContentController()
29
+ _ = controller.view
30
+
31
+ webView.addSubview(controller.view)
32
+
33
+ XCTAssertFalse(controller.host(webView: webView))
34
+ XCTAssertNil(webView.superview)
35
+ XCTAssertEqual(controller.view.superview, webView)
36
+ }
19
37
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-native-navigation",
3
- "version": "8.0.13",
3
+ "version": "8.0.15-beta.pr12.18.1",
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",
@@ -14,7 +14,7 @@
14
14
  "ios/Sources",
15
15
  "ios/Tests",
16
16
  "Package.swift",
17
- "CapgoNativeNavigation.podspec"
17
+ "CapgoCapacitorNativeNavigation.podspec"
18
18
  ],
19
19
  "author": "Martin Donadieu <martin@capgo.app>",
20
20
  "license": "MPL-2.0",
@@ -35,7 +35,7 @@
35
35
  ],
36
36
  "scripts": {
37
37
  "verify": "bun run verify:ios && bun run verify:android && bun run verify:web",
38
- "verify:ios": "xcodebuild -scheme CapgoNativeNavigation -destination generic/platform=iOS",
38
+ "verify:ios": "xcodebuild -scheme CapgoCapacitorNativeNavigation -destination generic/platform=iOS",
39
39
  "verify:android": "cd android && ./gradlew clean build test && cd ..",
40
40
  "verify:web": "bun run build",
41
41
  "lint": "bun run eslint && bun run prettier -- --check && bun run swiftlint -- lint",