@capgo/capacitor-native-navigation 8.0.15-beta.pr12.18.1 → 8.0.16
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.
|
@@ -438,6 +438,13 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
|
|
|
438
438
|
notifyTabSelect(index: item.tag)
|
|
439
439
|
}
|
|
440
440
|
|
|
441
|
+
public func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
|
|
442
|
+
if usesSystemLiquidGlass {
|
|
443
|
+
hostWebView(in: viewController)
|
|
444
|
+
}
|
|
445
|
+
return true
|
|
446
|
+
}
|
|
447
|
+
|
|
441
448
|
public func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
|
|
442
449
|
guard !suppressTabSelectEvent else {
|
|
443
450
|
hostWebViewInSelectedSystemTab()
|
|
@@ -580,8 +587,8 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
|
|
|
580
587
|
let previousFrame = webView.frame
|
|
581
588
|
let previousAutoresizingMask = webView.autoresizingMask
|
|
582
589
|
let container = UIView(frame: previousFrame)
|
|
583
|
-
container.backgroundColor =
|
|
584
|
-
container.isOpaque =
|
|
590
|
+
container.backgroundColor = nativeNavigationFallbackBackground(for: webView)
|
|
591
|
+
container.isOpaque = true
|
|
585
592
|
container.autoresizingMask = previousAutoresizingMask.isEmpty ? [.flexibleWidth, .flexibleHeight] : previousAutoresizingMask
|
|
586
593
|
|
|
587
594
|
if let previousSuperview = previousSuperview {
|
|
@@ -614,26 +621,45 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
|
|
|
614
621
|
}
|
|
615
622
|
|
|
616
623
|
let previousSelectedIndex = tabBarController.selectedIndex
|
|
617
|
-
let controllers = items
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
624
|
+
let controllers = systemTabContentControllers(for: items)
|
|
625
|
+
let currentControllers = tabBarController.viewControllers ?? []
|
|
626
|
+
let shouldUpdateControllers = currentControllers.count != controllers.count
|
|
627
|
+
|| zip(currentControllers, controllers).contains { currentController, nextController in
|
|
628
|
+
currentController !== nextController
|
|
629
|
+
}
|
|
622
630
|
let shouldAnimate = animated && tabBarController.viewControllers?.count == controllers.count
|
|
623
631
|
|
|
624
632
|
suppressTabSelectEvent = true
|
|
625
|
-
|
|
633
|
+
if shouldUpdateControllers {
|
|
634
|
+
tabBarController.setViewControllers(controllers, animated: shouldAnimate)
|
|
635
|
+
}
|
|
626
636
|
if !controllers.isEmpty {
|
|
627
637
|
let fallbackIndex = selectedIndex ?? previousSelectedIndex
|
|
628
638
|
let index = min(max(fallbackIndex, 0), controllers.count - 1)
|
|
639
|
+
hostWebView(in: controllers[index])
|
|
629
640
|
tabBarController.selectedIndex = index
|
|
630
641
|
}
|
|
631
|
-
hostWebViewInSelectedSystemTab()
|
|
632
642
|
suppressTabSelectEvent = false
|
|
633
643
|
|
|
634
644
|
tabViewControllers = controllers
|
|
635
645
|
}
|
|
636
646
|
|
|
647
|
+
private func systemTabContentControllers(for items: [UITabBarItem]) -> [UIViewController] {
|
|
648
|
+
let existingControllers = tabViewControllers.compactMap { $0 as? NativeNavigationTabContentController }
|
|
649
|
+
if existingControllers.count == items.count {
|
|
650
|
+
zip(existingControllers, items).forEach { controller, item in
|
|
651
|
+
controller.tabBarItem = item
|
|
652
|
+
}
|
|
653
|
+
return existingControllers
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
return items.map { item -> UIViewController in
|
|
657
|
+
let controller = NativeNavigationTabContentController()
|
|
658
|
+
controller.tabBarItem = item
|
|
659
|
+
return controller
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
637
663
|
private func setSystemTabBarHidden(_ hidden: Bool) {
|
|
638
664
|
guard let tabBarController = tabBarController else {
|
|
639
665
|
return
|
|
@@ -716,14 +742,18 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
|
|
|
716
742
|
}
|
|
717
743
|
|
|
718
744
|
private func hostWebViewInSelectedSystemTab() {
|
|
745
|
+
hostWebView(in: tabBarController?.selectedViewController)
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
private func hostWebView(in viewController: UIViewController?) {
|
|
719
749
|
guard usesSystemLiquidGlass,
|
|
720
750
|
let webView = webView,
|
|
721
|
-
let selectedController =
|
|
751
|
+
let selectedController = viewController as? NativeNavigationTabContentController else {
|
|
722
752
|
return
|
|
723
753
|
}
|
|
724
754
|
|
|
725
755
|
captureOriginalWebViewPlacementIfNeeded(webView)
|
|
726
|
-
clearHostedWebViews(matching: webView, except: selectedController)
|
|
756
|
+
clearHostedWebViews(matching: webView, except: selectedController, preservingSnapshots: true)
|
|
727
757
|
guard selectedController.host(webView: webView) else {
|
|
728
758
|
isWebViewHostedInSystemTabController = false
|
|
729
759
|
return
|
|
@@ -748,11 +778,15 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
|
|
|
748
778
|
isWebViewHostedInSystemTabController = false
|
|
749
779
|
}
|
|
750
780
|
|
|
751
|
-
private func clearHostedWebViews(
|
|
781
|
+
private func clearHostedWebViews(
|
|
782
|
+
matching webView: UIView,
|
|
783
|
+
except owner: NativeNavigationTabContentController? = nil,
|
|
784
|
+
preservingSnapshots: Bool = false
|
|
785
|
+
) {
|
|
752
786
|
tabViewControllers
|
|
753
787
|
.compactMap { $0 as? NativeNavigationTabContentController }
|
|
754
788
|
.filter { $0 !== owner }
|
|
755
|
-
.forEach { $0.clearHostedWebView(ifMatching: webView) }
|
|
789
|
+
.forEach { $0.clearHostedWebView(ifMatching: webView, preservingSnapshot: preservingSnapshots) }
|
|
756
790
|
}
|
|
757
791
|
|
|
758
792
|
private func makeBarButtonItems(_ rawItems: [[String: Any]], placement: String) -> [UIBarButtonItem] {
|
|
@@ -948,12 +982,12 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
|
|
|
948
982
|
|
|
949
983
|
private func transitionSnapshotView(from webView: UIView, sourceRect: CGRect?) -> UIView {
|
|
950
984
|
guard let sourceRect = sourceRect else {
|
|
951
|
-
return webView.snapshotView(afterScreenUpdates: false) ??
|
|
985
|
+
return webView.snapshotView(afterScreenUpdates: false) ?? nativeNavigationSnapshotPlaceholder(for: webView)
|
|
952
986
|
}
|
|
953
987
|
|
|
954
988
|
let cropRect = sourceRect.intersection(webView.bounds)
|
|
955
989
|
guard cropRect.width > 0, cropRect.height > 0 else {
|
|
956
|
-
return webView.snapshotView(afterScreenUpdates: false) ??
|
|
990
|
+
return webView.snapshotView(afterScreenUpdates: false) ?? nativeNavigationSnapshotPlaceholder(for: webView)
|
|
957
991
|
}
|
|
958
992
|
|
|
959
993
|
let renderer = UIGraphicsImageRenderer(bounds: webView.bounds)
|
|
@@ -969,7 +1003,7 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
|
|
|
969
1003
|
).integral
|
|
970
1004
|
|
|
971
1005
|
guard let croppedImage = image.cgImage?.cropping(to: scaledCropRect) else {
|
|
972
|
-
return webView.snapshotView(afterScreenUpdates: false) ??
|
|
1006
|
+
return webView.snapshotView(afterScreenUpdates: false) ?? nativeNavigationSnapshotPlaceholder(for: webView)
|
|
973
1007
|
}
|
|
974
1008
|
|
|
975
1009
|
let imageView = UIImageView(image: UIImage(cgImage: croppedImage, scale: scale, orientation: image.imageOrientation))
|
|
@@ -1418,19 +1452,20 @@ private final class NativeNavigationBar: UINavigationBar {
|
|
|
1418
1452
|
final class NativeNavigationTabController: UITabBarController {
|
|
1419
1453
|
override func viewDidLoad() {
|
|
1420
1454
|
super.viewDidLoad()
|
|
1421
|
-
view.backgroundColor = .
|
|
1422
|
-
view.isOpaque =
|
|
1455
|
+
view.backgroundColor = .systemBackground
|
|
1456
|
+
view.isOpaque = true
|
|
1423
1457
|
tabBar.isTranslucent = true
|
|
1424
1458
|
}
|
|
1425
1459
|
}
|
|
1426
1460
|
|
|
1427
1461
|
final class NativeNavigationTabContentController: UIViewController {
|
|
1428
1462
|
private weak var hostedWebView: UIView?
|
|
1463
|
+
private var snapshotPlaceholder: UIView?
|
|
1429
1464
|
|
|
1430
1465
|
override func loadView() {
|
|
1431
1466
|
let view = UIView()
|
|
1432
|
-
view.backgroundColor = .
|
|
1433
|
-
view.isOpaque =
|
|
1467
|
+
view.backgroundColor = .systemBackground
|
|
1468
|
+
view.isOpaque = true
|
|
1434
1469
|
self.view = view
|
|
1435
1470
|
}
|
|
1436
1471
|
|
|
@@ -1443,10 +1478,20 @@ final class NativeNavigationTabContentController: UIViewController {
|
|
|
1443
1478
|
hostedWebView?.frame = view.bounds
|
|
1444
1479
|
}
|
|
1445
1480
|
|
|
1446
|
-
func clearHostedWebView(ifMatching webView: UIView? = nil) {
|
|
1481
|
+
func clearHostedWebView(ifMatching webView: UIView? = nil, preservingSnapshot: Bool = false) {
|
|
1447
1482
|
guard webView == nil || hostedWebView === webView else {
|
|
1448
1483
|
return
|
|
1449
1484
|
}
|
|
1485
|
+
|
|
1486
|
+
if preservingSnapshot, let hostedWebView = hostedWebView, hostedWebView.superview === view {
|
|
1487
|
+
let placeholder = hostedWebView.snapshotView(afterScreenUpdates: false) ?? nativeNavigationSnapshotPlaceholder(for: hostedWebView)
|
|
1488
|
+
placeholder.frame = hostedWebView.frame
|
|
1489
|
+
placeholder.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
1490
|
+
snapshotPlaceholder?.removeFromSuperview()
|
|
1491
|
+
view.insertSubview(placeholder, belowSubview: hostedWebView)
|
|
1492
|
+
snapshotPlaceholder = placeholder
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1450
1495
|
hostedWebView = nil
|
|
1451
1496
|
}
|
|
1452
1497
|
|
|
@@ -1457,6 +1502,8 @@ final class NativeNavigationTabContentController: UIViewController {
|
|
|
1457
1502
|
return false
|
|
1458
1503
|
}
|
|
1459
1504
|
|
|
1505
|
+
snapshotPlaceholder?.removeFromSuperview()
|
|
1506
|
+
snapshotPlaceholder = nil
|
|
1460
1507
|
hostedWebView = webView
|
|
1461
1508
|
if webView.superview !== view {
|
|
1462
1509
|
webView.removeFromSuperview()
|
|
@@ -1468,6 +1515,21 @@ final class NativeNavigationTabContentController: UIViewController {
|
|
|
1468
1515
|
}
|
|
1469
1516
|
}
|
|
1470
1517
|
|
|
1518
|
+
private func nativeNavigationFallbackBackground(for view: UIView) -> UIColor {
|
|
1519
|
+
if let color = view.backgroundColor,
|
|
1520
|
+
color.cgColor.alpha > 0 {
|
|
1521
|
+
return color
|
|
1522
|
+
}
|
|
1523
|
+
return .systemBackground
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
private func nativeNavigationSnapshotPlaceholder(for view: UIView) -> UIView {
|
|
1527
|
+
let placeholder = UIView(frame: view.frame)
|
|
1528
|
+
placeholder.backgroundColor = nativeNavigationFallbackBackground(for: view)
|
|
1529
|
+
placeholder.isOpaque = true
|
|
1530
|
+
return placeholder
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1471
1533
|
private final class SVGIconRenderer: NSObject, XMLParserDelegate {
|
|
1472
1534
|
private let context: CGContext
|
|
1473
1535
|
private var styleStack = [SVGRenderStyle()]
|
|
@@ -23,6 +23,34 @@ class NativeNavigationTests: XCTestCase {
|
|
|
23
23
|
XCTAssertEqual(webView.frame, controller.view.bounds)
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
func testTabContentControllerKeepsSnapshotPlaceholderWhenWebViewMoves() {
|
|
27
|
+
let webView = UIView()
|
|
28
|
+
let firstController = NativeNavigationTabContentController()
|
|
29
|
+
let secondController = NativeNavigationTabContentController()
|
|
30
|
+
_ = firstController.view
|
|
31
|
+
_ = secondController.view
|
|
32
|
+
|
|
33
|
+
firstController.view.frame = CGRect(x: 0, y: 0, width: 320, height: 480)
|
|
34
|
+
secondController.view.frame = CGRect(x: 0, y: 0, width: 320, height: 480)
|
|
35
|
+
webView.backgroundColor = .systemBackground
|
|
36
|
+
|
|
37
|
+
XCTAssertTrue(firstController.host(webView: webView))
|
|
38
|
+
XCTAssertEqual(firstController.view.subviews.count, 1)
|
|
39
|
+
|
|
40
|
+
firstController.clearHostedWebView(ifMatching: webView, preservingSnapshot: true)
|
|
41
|
+
XCTAssertEqual(firstController.view.subviews.count, 2)
|
|
42
|
+
|
|
43
|
+
XCTAssertTrue(secondController.host(webView: webView))
|
|
44
|
+
XCTAssertEqual(webView.superview, secondController.view)
|
|
45
|
+
XCTAssertEqual(firstController.view.subviews.count, 1)
|
|
46
|
+
XCTAssertFalse(firstController.view.subviews.contains(webView))
|
|
47
|
+
|
|
48
|
+
XCTAssertTrue(firstController.host(webView: webView))
|
|
49
|
+
XCTAssertEqual(webView.superview, firstController.view)
|
|
50
|
+
XCTAssertEqual(firstController.view.subviews.count, 1)
|
|
51
|
+
XCTAssertTrue(firstController.view.subviews.first === webView)
|
|
52
|
+
}
|
|
53
|
+
|
|
26
54
|
func testTabContentControllerRejectsLayerCycle() {
|
|
27
55
|
let webView = UIView()
|
|
28
56
|
let controller = NativeNavigationTabContentController()
|
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.16",
|
|
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",
|
|
@@ -26,6 +26,9 @@
|
|
|
26
26
|
"url": "https://github.com/Cap-go/capacitor-native-navigation/issues"
|
|
27
27
|
},
|
|
28
28
|
"homepage": "https://capgo.app/docs/plugins/native-navigation/",
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=22.0.0"
|
|
31
|
+
},
|
|
29
32
|
"keywords": [
|
|
30
33
|
"capacitor",
|
|
31
34
|
"native-navigation",
|