@ajuarezso/capacitor-liquid-glass 0.2.0 → 0.3.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.
@@ -16,8 +16,11 @@ export interface LiquidGlassTabItem {
16
16
  * more see-through than default.
17
17
  * - `'transparent'`: no background, no blur — content behind shows through 100%.
18
18
  * Trade-off: legibilidad puede sufrir sobre contenido caótico.
19
+ * - `'liquidGlass'`: REAL iOS 26 `UIGlassEffect` — el mismo material que usan
20
+ * Music y App Store. En iOS < 26 cae a `UIBlurEffect.systemThinMaterial`
21
+ * (aproximación cercana con system vibrancy).
19
22
  */
20
- export type TabBarStyle = 'default' | 'ultraThin' | 'transparent';
23
+ export type TabBarStyle = 'default' | 'ultraThin' | 'transparent' | 'liquidGlass';
21
24
  export interface ShowTabBarOptions {
22
25
  items: LiquidGlassTabItem[];
23
26
  /** Index of the initially selected item. Defaults to 0. */
@@ -29,6 +29,9 @@ enum LiquidGlassTabBarStyle: String {
29
29
  case ultraThin
30
30
  /// No background, no blur — content behind shows through 100%.
31
31
  case transparent
32
+ /// REAL iOS 26 `UIGlassEffect` (Music + App Store material).
33
+ /// Fallback en iOS < 26 → `UIBlurEffect.systemThinMaterial`.
34
+ case liquidGlass
32
35
  }
33
36
 
34
37
  /// A floating UITabBar overlay that adopts iOS 26 Liquid Glass automatically
@@ -44,6 +47,11 @@ final class LiquidGlassTabBarOverlay: NSObject {
44
47
  private weak var window: UIWindow?
45
48
  private var tabBar: UITabBar?
46
49
  private var items: [LiquidGlassTabItem] = []
50
+ /// Overlay `UIVisualEffectView` que provee el material cuando el style es
51
+ /// `.liquidGlass`. Solo se monta cuando el estilo lo requiere, y se
52
+ /// destruye al cambiar a otro estilo. Vive como subview del window justo
53
+ /// DEBAJO del `tabBar` (que en este modo queda 100% transparente).
54
+ private var glassOverlayView: UIVisualEffectView?
47
55
 
48
56
  func attach(to window: UIWindow) {
49
57
  if self.window === window, tabBar != nil { return }
@@ -73,20 +81,36 @@ final class LiquidGlassTabBarOverlay: NSObject {
73
81
  /// - `default`: Liquid Glass (iOS 26+) o blur translúcido como fallback.
74
82
  /// - `ultraThin`: blur mínimo `systemUltraThinMaterial` — más ver-through.
75
83
  /// - `transparent`: sin background ni blur — content behind se ve completo.
84
+ /// - `liquidGlass`: tab bar 100% transparente + overlay `UIVisualEffectView`
85
+ /// con `UIGlassEffect` (iOS 26+) o `systemThinMaterial` fallback.
76
86
  private func applyAppearance(_ tabBar: UITabBar, style: LiquidGlassTabBarStyle) {
77
87
  let appearance = UITabBarAppearance()
78
88
  switch style {
79
89
  case .default:
80
90
  appearance.configureWithDefaultBackground()
91
+ removeGlassOverlay()
81
92
  case .ultraThin:
82
93
  appearance.configureWithDefaultBackground()
83
94
  appearance.backgroundEffect = UIBlurEffect(style: .systemUltraThinMaterial)
84
95
  appearance.backgroundColor = UIColor.clear
96
+ removeGlassOverlay()
85
97
  case .transparent:
86
98
  appearance.configureWithTransparentBackground()
87
99
  appearance.backgroundEffect = nil
88
100
  appearance.backgroundColor = UIColor.clear
89
101
  appearance.shadowColor = UIColor.clear
102
+ removeGlassOverlay()
103
+ case .liquidGlass:
104
+ // Tab bar 100% transparente — el material lo aporta el overlay
105
+ // `UIVisualEffectView` con `UIGlassEffect` montado debajo del bar.
106
+ // Es el patrón canon de iOS 26 para aplicar Liquid Glass real a
107
+ // controles que no exponen API directa para asignarlo (UITabBar
108
+ // solo acepta `UIBlurEffect` en `backgroundEffect`).
109
+ appearance.configureWithTransparentBackground()
110
+ appearance.backgroundEffect = nil
111
+ appearance.backgroundColor = UIColor.clear
112
+ appearance.shadowColor = UIColor.clear
113
+ installGlassOverlay(behind: tabBar)
90
114
  }
91
115
  tabBar.standardAppearance = appearance
92
116
  if #available(iOS 15.0, *) {
@@ -94,6 +118,50 @@ final class LiquidGlassTabBarOverlay: NSObject {
94
118
  }
95
119
  }
96
120
 
121
+ /// Monta un `UIVisualEffectView` con `UIGlassEffect` (iOS 26+) o
122
+ /// `systemThinMaterial` como fallback, justo DEBAJO del `tabBar` en el
123
+ /// z-order del window. `tabBar` está en modo transparente, así que todo
124
+ /// el material visible viene del overlay.
125
+ ///
126
+ /// El overlay queda con `isUserInteractionEnabled = false` para no
127
+ /// interferir con los taps que el `tabBar` consume por encima.
128
+ private func installGlassOverlay(behind tabBar: UITabBar) {
129
+ guard let parent = tabBar.superview else { return }
130
+ // Idempotente: si ya existe overlay no recrearlo (evita flicker en
131
+ // re-configs por badge updates u otros cambios sin style change).
132
+ if glassOverlayView != nil { return }
133
+
134
+ let effect: UIVisualEffect
135
+ #if compiler(>=6.1)
136
+ if #available(iOS 26.0, *) {
137
+ effect = UIGlassEffect()
138
+ } else {
139
+ effect = UIBlurEffect(style: .systemThinMaterial)
140
+ }
141
+ #else
142
+ effect = UIBlurEffect(style: .systemThinMaterial)
143
+ #endif
144
+
145
+ let overlay = UIVisualEffectView(effect: effect)
146
+ overlay.translatesAutoresizingMaskIntoConstraints = false
147
+ overlay.isUserInteractionEnabled = false
148
+ parent.insertSubview(overlay, belowSubview: tabBar)
149
+
150
+ NSLayoutConstraint.activate([
151
+ overlay.leadingAnchor.constraint(equalTo: tabBar.leadingAnchor),
152
+ overlay.trailingAnchor.constraint(equalTo: tabBar.trailingAnchor),
153
+ overlay.topAnchor.constraint(equalTo: tabBar.topAnchor),
154
+ overlay.bottomAnchor.constraint(equalTo: tabBar.bottomAnchor),
155
+ ])
156
+
157
+ self.glassOverlayView = overlay
158
+ }
159
+
160
+ private func removeGlassOverlay() {
161
+ glassOverlayView?.removeFromSuperview()
162
+ glassOverlayView = nil
163
+ }
164
+
97
165
  func configure(items: [LiquidGlassTabItem], selectedIndex: Int, tintHex: String?, style: LiquidGlassTabBarStyle) {
98
166
  guard let tabBar else { return }
99
167
 
@@ -153,6 +221,12 @@ final class LiquidGlassTabBarOverlay: NSObject {
153
221
  tabBar.isHidden = false
154
222
  tabBar.alpha = 0
155
223
  tabBar.transform = CGAffineTransform(translationX: 0, y: 20)
224
+ // Sincronizar el overlay glass (si está montado) con la misma curva,
225
+ // sino el material queda visible mientras el bar fade-out/in.
226
+ let overlay = glassOverlayView
227
+ overlay?.isHidden = false
228
+ overlay?.alpha = 0
229
+ overlay?.transform = CGAffineTransform(translationX: 0, y: 20)
156
230
  // Fade-in + slide-up (curva native iOS para apariciones)
157
231
  UIView.animate(
158
232
  withDuration: 0.28,
@@ -163,6 +237,8 @@ final class LiquidGlassTabBarOverlay: NSObject {
163
237
  animations: {
164
238
  tabBar.alpha = 1
165
239
  tabBar.transform = .identity
240
+ overlay?.alpha = 1
241
+ overlay?.transform = .identity
166
242
  },
167
243
  completion: nil
168
244
  )
@@ -173,6 +249,7 @@ final class LiquidGlassTabBarOverlay: NSObject {
173
249
  guard let tabBar = tabBar else { return }
174
250
  if tabBar.isHidden { return }
175
251
  // Fade-out + slide-down (más rápido que el show — el exit es snappy)
252
+ let overlay = glassOverlayView
176
253
  UIView.animate(
177
254
  withDuration: 0.18,
178
255
  delay: 0,
@@ -180,10 +257,14 @@ final class LiquidGlassTabBarOverlay: NSObject {
180
257
  animations: {
181
258
  tabBar.alpha = 0
182
259
  tabBar.transform = CGAffineTransform(translationX: 0, y: 20)
260
+ overlay?.alpha = 0
261
+ overlay?.transform = CGAffineTransform(translationX: 0, y: 20)
183
262
  },
184
263
  completion: { _ in
185
264
  tabBar.isHidden = true
186
265
  tabBar.transform = .identity
266
+ overlay?.isHidden = true
267
+ overlay?.transform = .identity
187
268
  }
188
269
  )
189
270
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ajuarezso/capacitor-liquid-glass",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "iOS 26 Liquid Glass native chrome (TabBar, NavigationBar, Alerts, Sheets) for Capacitor apps. Falls back gracefully on iOS < 26 and Android.",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",