@ajuarezso/capacitor-liquid-glass 0.3.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.
@@ -47,6 +47,11 @@ final class LiquidGlassTabBarOverlay: NSObject {
47
47
  private weak var window: UIWindow?
48
48
  private var tabBar: UITabBar?
49
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?
50
55
 
51
56
  func attach(to window: UIWindow) {
52
57
  if self.window === window, tabBar != nil { return }
@@ -76,37 +81,36 @@ final class LiquidGlassTabBarOverlay: NSObject {
76
81
  /// - `default`: Liquid Glass (iOS 26+) o blur translúcido como fallback.
77
82
  /// - `ultraThin`: blur mínimo `systemUltraThinMaterial` — más ver-through.
78
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.
79
86
  private func applyAppearance(_ tabBar: UITabBar, style: LiquidGlassTabBarStyle) {
80
87
  let appearance = UITabBarAppearance()
81
88
  switch style {
82
89
  case .default:
83
90
  appearance.configureWithDefaultBackground()
91
+ removeGlassOverlay()
84
92
  case .ultraThin:
85
93
  appearance.configureWithDefaultBackground()
86
94
  appearance.backgroundEffect = UIBlurEffect(style: .systemUltraThinMaterial)
87
95
  appearance.backgroundColor = UIColor.clear
96
+ removeGlassOverlay()
88
97
  case .transparent:
89
98
  appearance.configureWithTransparentBackground()
90
99
  appearance.backgroundEffect = nil
91
100
  appearance.backgroundColor = UIColor.clear
92
101
  appearance.shadowColor = UIColor.clear
102
+ removeGlassOverlay()
93
103
  case .liquidGlass:
94
- // REAL iOS 26 Liquid Glass — el mismo material que usan Music y
95
- // App Store. `UIGlassEffect` solo está disponible compilando con
96
- // el SDK iOS 26+ (Xcode 26+). En iOS < 26 runtime cae a
97
- // `systemThinMaterial`, que es la mejor aproximación legacy con
98
- // vibrancy del sistema.
99
- appearance.configureWithDefaultBackground()
100
- #if compiler(>=6.1)
101
- if #available(iOS 26.0, *) {
102
- appearance.backgroundEffect = UIGlassEffect()
103
- } else {
104
- appearance.backgroundEffect = UIBlurEffect(style: .systemThinMaterial)
105
- }
106
- #else
107
- appearance.backgroundEffect = UIBlurEffect(style: .systemThinMaterial)
108
- #endif
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
109
111
  appearance.backgroundColor = UIColor.clear
112
+ appearance.shadowColor = UIColor.clear
113
+ installGlassOverlay(behind: tabBar)
110
114
  }
111
115
  tabBar.standardAppearance = appearance
112
116
  if #available(iOS 15.0, *) {
@@ -114,6 +118,50 @@ final class LiquidGlassTabBarOverlay: NSObject {
114
118
  }
115
119
  }
116
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
+
117
165
  func configure(items: [LiquidGlassTabItem], selectedIndex: Int, tintHex: String?, style: LiquidGlassTabBarStyle) {
118
166
  guard let tabBar else { return }
119
167
 
@@ -173,6 +221,12 @@ final class LiquidGlassTabBarOverlay: NSObject {
173
221
  tabBar.isHidden = false
174
222
  tabBar.alpha = 0
175
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)
176
230
  // Fade-in + slide-up (curva native iOS para apariciones)
177
231
  UIView.animate(
178
232
  withDuration: 0.28,
@@ -183,6 +237,8 @@ final class LiquidGlassTabBarOverlay: NSObject {
183
237
  animations: {
184
238
  tabBar.alpha = 1
185
239
  tabBar.transform = .identity
240
+ overlay?.alpha = 1
241
+ overlay?.transform = .identity
186
242
  },
187
243
  completion: nil
188
244
  )
@@ -193,6 +249,7 @@ final class LiquidGlassTabBarOverlay: NSObject {
193
249
  guard let tabBar = tabBar else { return }
194
250
  if tabBar.isHidden { return }
195
251
  // Fade-out + slide-down (más rápido que el show — el exit es snappy)
252
+ let overlay = glassOverlayView
196
253
  UIView.animate(
197
254
  withDuration: 0.18,
198
255
  delay: 0,
@@ -200,10 +257,14 @@ final class LiquidGlassTabBarOverlay: NSObject {
200
257
  animations: {
201
258
  tabBar.alpha = 0
202
259
  tabBar.transform = CGAffineTransform(translationX: 0, y: 20)
260
+ overlay?.alpha = 0
261
+ overlay?.transform = CGAffineTransform(translationX: 0, y: 20)
203
262
  },
204
263
  completion: { _ in
205
264
  tabBar.isHidden = true
206
265
  tabBar.transform = .identity
266
+ overlay?.isHidden = true
267
+ overlay?.transform = .identity
207
268
  }
208
269
  )
209
270
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ajuarezso/capacitor-liquid-glass",
3
- "version": "0.3.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",