¿Qué es Liquid Glass y por qué cambia todo?
Liquid Glass es, sin exagerar, el cambio visual más importante en iOS desde que Apple eliminó el skeumorfismo con iOS 7 allá por 2013. Se presentó en la WWDC 2025 y, desde entonces, ha transformado por completo la apariencia de las apps en iOS 26, iPadOS 26, macOS Tahoe, watchOS 26 y visionOS 26.
Y aquí viene lo bueno: si tu app ya usa componentes nativos de SwiftUI, gran parte del trabajo ya está hecho.
Pero vamos al grano. ¿Qué es exactamente? Liquid Glass es un meta-material digital que simula vidrio real. Las superficies refractan la luz, distorsionan sutilmente el contenido que hay detrás, y responden dinámicamente al movimiento del dispositivo y a los gestos del usuario. A diferencia del blur tradicional que simplemente dispersa la luz, Liquid Glass la concentra y dobla mediante un efecto llamado lensing — creando una sensación de profundidad y materialidad que, sinceramente, no habíamos visto antes en interfaces móviles.
La filosofía de diseño es clara: Liquid Glass pertenece exclusivamente a la capa de navegación. Esos elementos que flotan por encima del contenido de tu app — barras de herramientas, tab bars, botones flotantes, sheets y popovers. Nunca se aplica al contenido en sí. Esta distinción es fundamental, y confundirla es probablemente el error más común que estamos viendo en las primeras implementaciones.
Lo que obtienes gratis: adopción automática
Antes de que te pongas a escribir código nuevo, tengo buenas noticias. Si tu app usa componentes estándar de SwiftUI o UIKit, al recompilar con Xcode 26 contra el SDK de iOS 26, obtienes Liquid Glass automáticamente en estos elementos:
- NavigationBar — la barra de navegación ahora flota con efecto de vidrio sobre tu contenido
- TabBar — se transforma en una pastilla flotante que se colapsa inteligentemente al hacer scroll
- Toolbar — los items se agrupan automáticamente sobre una superficie de cristal
- Sheets y Popovers — las presentaciones modales adoptan bordes de cristal adaptativo
- Menus, Alerts, Search bars — controles del sistema con el nuevo acabado
- Toggles, Sliders, Pickers — controles de entrada con transparencia dinámica
En macOS Tahoe, el efecto se extiende también al Dock, la barra de menús, las barras laterales y los controles de ventana. Literalmente, recompilas y tu app ya se ve diferente. Sin tocar una línea.
La API central: el modificador glassEffect()
Cuando necesitas aplicar Liquid Glass a tus propias vistas personalizadas, el modificador .glassEffect() es tu herramienta principal. Así es su firma completa:
func glassEffect<S: Shape>(
_ effect: GlassEffect = .regular,
in shape: S = DefaultGlassEffectShape,
isEnabled: Bool = true
) -> some View
En su uso más básico, solo necesitas añadirlo al final de tu cadena de modificadores:
import SwiftUI
struct BotonFlotante: View {
var body: some View {
Button("Crear Nuevo") {
// acción
}
.padding(.horizontal, 24)
.padding(.vertical, 12)
.glassEffect()
}
}
Fíjate en algo importante: no necesitas dibujar explícitamente un RoundedRectangle ni un Circle como fondo. El modificador .glassEffect() genera automáticamente una forma con esquinas redondeadas que se adapta al contexto de tu app. Esto es un cambio bastante significativo respecto a los patrones anteriores donde construías manualmente fondos translúcidos con .background(.ultraThinMaterial).
Variantes del efecto
Existen dos variantes principales de GlassEffect, y hay una regla que no deberías romper: nunca las mezcles en una misma interfaz.
.regular— La variante por defecto. Funciona para la mayoría de situaciones. Proporciona una superficie translúcida con refracción de luz natural..clear— Pensada exclusivamente para contenido rico en medios (fotos, vídeos) donde el fondo no sufre por el oscurecimiento. Los elementos que pongas encima deben tener suficiente peso visual.
Apple insiste mucho en este punto: si usas .regular en una pantalla, no uses .clear en esa misma pantalla. Cada variante tiene sus propias características ópticas y mezclarlas produce resultados visuales que, bueno, se ven raros.
Formas personalizadas
Puedes definir la forma del cristal usando cualquier tipo que conforme el protocolo Shape:
// Cápsula (por defecto)
Button("Inicio") { }
.glassEffect(.regular, in: .capsule)
// Rectángulo con esquinas redondeadas
Button("Guardar") { }
.glassEffect(.regular, in: .rect(cornerRadius: 16))
// Círculo
Button { } label: {
Image(systemName: "plus")
.frame(width: 56, height: 56)
}
.glassEffect(.regular, in: .circle)
Tintes semánticos: color con propósito
Una de las características más elegantes de Liquid Glass (y de mis favoritas, siendo honesto) es el sistema de tintes. Puedes colorear una superficie de cristal para comunicar significado, pero con una regla de oro: solo tiñe la acción principal. Si todo tiene color, nada destaca.
struct BarraDeAcciones: View {
var body: some View {
HStack(spacing: 16) {
// Acción secundaria: sin tinte
Button("Cancelar") {
// ...
}
.buttonStyle(.glass)
// Acción principal: con tinte azul
Button("Guardar Cambios") {
// ...
}
.buttonStyle(.glassProminent)
.tint(.blue)
}
}
}
iOS 26 introduce dos estilos de botón nuevos:
.glass— Translúcido, para acciones secundarias..glassProminent— Más opaco y con mayor presencia visual, pensado para la acción principal.
También puedes aplicar tintes directamente al modificador .glassEffect() para vistas que no son botones:
// Tinte sutil con opacidad reducida
Text("3 elementos seleccionados")
.padding()
.glassEffect(.regular.tint(.blue.opacity(0.8)))
// Tinte para indicar estado de éxito
Label("Completado", systemImage: "checkmark.circle")
.padding()
.glassEffect(.regular.tint(.green))
La recomendación general es usar tintes solo para transmitir significado: éxito (verde), advertencia (amarillo), acción destructiva (rojo), acción principal (azul). Nunca como decoración pura.
Modo interactivo: feedback táctil en iOS
El modificador .interactive() es de esas cosas que tienes que ver para entender. Activa una capa de retroalimentación visual exclusiva de iOS que incluye escalado al presionar, rebote elástico al soltar y un efecto shimmer (un destello que recorre la superficie del cristal). Queda especialmente bien en botones de acción flotante.
struct BotonAccionFlotante: View {
var body: some View {
Button {
// acción principal
} label: {
Image(systemName: "plus")
.font(.title2)
.foregroundStyle(.white)
.frame(width: 56, height: 56)
}
.glassEffect(.regular.tint(.purple.opacity(0.8)).interactive())
}
}
Puedes encadenar tinte e interactividad sin problema: .glassEffect(.regular.tint(.blue).interactive()). El orden no importa — el resultado es una superficie con color y respuesta táctil.
GlassEffectContainer: agrupación y fusión visual
Aquí es donde Liquid Glass realmente brilla. El GlassEffectContainer es un contenedor que agrupa múltiples elementos de cristal y te da tres beneficios clave:
- Región de muestreo compartida — Los elementos dentro del contenedor comparten la misma referencia visual del fondo, lo que garantiza una apariencia consistente.
- Renderizado optimizado — El sistema hace un solo pase de renderizado para todo el grupo. Rendimiento, vamos.
- Fusión fluida — Los elementos que se acercan entre sí se fusionan visualmente, como gotas de agua que se unen, y se separan cuando se alejan. Es un detalle precioso.
Pero hay un punto técnico importante: el cristal no puede muestrear otro cristal. Si colocas elementos de cristal uno sobre otro sin un contenedor, vas a obtener resultados visuales incorrectos. El contenedor resuelve esto.
struct MenuFlotante: View {
@State private var expandido = false
var body: some View {
GlassEffectContainer(spacing: 8) {
if expandido {
Button {
// cámara
} label: {
Image(systemName: "camera")
.frame(width: 44, height: 44)
}
.glassEffect(.regular.interactive())
Button {
// galería
} label: {
Image(systemName: "photo")
.frame(width: 44, height: 44)
}
.glassEffect(.regular.interactive())
Button {
// documento
} label: {
Image(systemName: "doc")
.frame(width: 44, height: 44)
}
.glassEffect(.regular.interactive())
}
Button {
withAnimation(.spring(duration: 0.4, bounce: 0.2)) {
expandido.toggle()
}
} label: {
Image(systemName: expandido ? "xmark" : "plus")
.font(.title2)
.frame(width: 56, height: 56)
.contentTransition(.symbolEffect(.replace))
}
.glassEffect(
.regular.tint(.purple.opacity(0.8)).interactive()
)
}
}
}
El parámetro spacing del contenedor controla la distancia mínima (en puntos) a la que los elementos empiezan a fusionarse visualmente. Si dos elementos están a menos de esa distancia, se mezclan en una sola superficie de cristal. Un detalle muy cuidado.
Transiciones morfológicas con glassEffectID
Las transiciones morfológicas son, bajo mi punto de vista, lo más impresionante de Liquid Glass. Permiten que un elemento de cristal se transforme fluidamente en otro, con la superficie estirándose y contrayéndose entre estados. Para que funcionen necesitas tres cosas:
- Los elementos deben compartir el mismo
GlassEffectContainer. - Cada elemento necesita un
glassEffectIDúnico dentro de un@Namespacecompartido. - Los cambios de estado tienen que envolverse en un bloque de animación.
struct BarraExpandible: View {
@State private var modoEdicion = false
@Namespace private var animacion
var body: some View {
GlassEffectContainer(spacing: 20) {
HStack(spacing: 16) {
if modoEdicion {
Button("Cortar", systemImage: "scissors") { }
.glassEffect(.regular.interactive())
.glassEffectID("cortar", in: animacion)
Button("Copiar", systemImage: "doc.on.doc") { }
.glassEffect(.regular.interactive())
.glassEffectID("copiar", in: animacion)
Button("Pegar", systemImage: "doc.on.clipboard") { }
.glassEffect(.regular.interactive())
.glassEffectID("pegar", in: animacion)
}
Button {
withAnimation(.bouncy) {
modoEdicion.toggle()
}
} label: {
Image(systemName: modoEdicion ? "xmark" : "pencil")
.contentTransition(.symbolEffect(.replace))
}
.buttonStyle(.glassProminent)
.tint(.blue)
.glassEffectID("toggle", in: animacion)
}
}
}
}
Cuando el usuario toca el botón de edición, los botones de acción emergen del botón principal con una transición fluida del cristal. Al volver a tocarlo, se reabsorben de vuelta. Este tipo de microinteracciones es lo que hace que las apps en iOS 26 se sientan realmente vivas.
Errores frecuentes (y cómo evitarlos)
Después de revisar bastantes implementaciones tempranas de Liquid Glass, estos son los errores que más se repiten. Si puedes evitarlos, te ahorrarás un buen rato de depuración visual.
1. Aplicar cristal al contenido en lugar de a la navegación
// ❌ INCORRECTO: cristal en filas de lista
List {
ForEach(items) { item in
Text(item.nombre)
.padding()
.glassEffect() // No hagas esto
}
}
// ✅ CORRECTO: cristal solo en controles flotantes
ZStack {
List {
ForEach(items) { item in
Text(item.nombre)
}
}
VStack {
Spacer()
Button("Añadir") { }
.glassEffect(.regular.interactive())
.padding()
}
}
2. Poner un fondo opaco antes de glassEffect
// ❌ El fondo bloquea el efecto de cristal
Button("Acción") { }
.frame(width: 50, height: 50)
.background(Circle().fill(.purple))
.glassEffect()
// ✅ Usa tinte en su lugar
Button("Acción") { }
.frame(width: 50, height: 50)
.glassEffect(.regular.tint(.purple.opacity(0.8)).interactive())
3. Mezclar variantes .regular y .clear
// ❌ Nunca mezcles variantes en la misma pantalla
HStack {
Button("A") { }.glassEffect(.regular)
Button("B") { }.glassEffect(.clear)
}
// ✅ Usa una sola variante consistente
HStack {
Button("A") { }.glassEffect(.regular)
Button("B") { }.glassEffect(.regular)
}
4. Múltiples elementos de cristal sin contenedor
Este es más sutil pero igual de problemático:
// ❌ Muestreo inconsistente del fondo
HStack {
Button("A") { }.glassEffect()
Button("B") { }.glassEffect()
}
// ✅ Agrupa en un contenedor
GlassEffectContainer {
HStack {
Button("A") { }.glassEffect()
Button("B") { }.glassEffect()
}
}
5. Apilar capas de cristal
Superponer demasiadas capas de cristal (por ejemplo, una tab bar + tarjeta + sheet) acumula problemas de contraste rápidamente. La solución es moderación y espaciado. Recuerda siempre: el cristal no puede muestrear otro cristal.
Accesibilidad: funciona sin que hagas nada
Apple ha integrado el soporte de accesibilidad directamente en el material de Liquid Glass. Y lo mejor es que funciona sin que tengas que escribir una sola línea de código:
- Reducir transparencia (Reduce Transparency) — El cristal adopta un aspecto más opaco y esmerilado, manteniendo la legibilidad.
- Aumentar contraste (Increase Contrast) — Las superficies pasan a blanco y negro sólido con bordes bien visibles.
- Reducir movimiento (Reduce Motion) — Los efectos elásticos y de rebote se desactivan automáticamente.
Esta adaptación automática es una de las razones principales por las que Apple recomienda usar las APIs oficiales en lugar de intentar replicar el efecto con materiales y blurs personalizados. No reinventes la rueda aquí.
Compatibilidad hacia atrás y migración
Si tu app necesita soportar versiones anteriores a iOS 26, las APIs de Liquid Glass no van a compilar en SDKs más antiguos. Pero no te preocupes, hay una estrategia bastante limpia: crear modificadores de vista condicionales.
extension View {
@ViewBuilder
func adaptiveGlass() -> some View {
if #available(iOS 26, *) {
self.glassEffect(.regular.interactive())
} else {
self
.background(.ultraThinMaterial)
.clipShape(RoundedRectangle(cornerRadius: 12))
}
}
}
Y si decides que tu app todavía no está lista para adoptar Liquid Glass, Apple te da una válvula de escape temporal. En tu Info.plist, añade la clave UIDesignRequiresCompatability como Boolean con valor YES. Esto desactiva Liquid Glass en toda la aplicación.
Eso sí, un aviso: Apple ha dejado claro que esta opción estará disponible solo hasta la siguiente versión mayor del sistema. Así que es un parche, no una solución permanente.
Ejemplo completo: pantalla de perfil con controles de cristal
Vamos a poner todo junto. Construyamos una pantalla de perfil realista que combine todos los patrones que hemos visto:
import SwiftUI
struct PerfilView: View {
@State private var mostrarOpciones = false
@Namespace private var transicion
var body: some View {
ZStack {
// Capa de contenido
ScrollView {
VStack(spacing: 20) {
// Imagen de perfil
Image("avatar")
.resizable()
.scaledToFill()
.frame(width: 120, height: 120)
.clipShape(Circle())
.padding(.top, 40)
Text("María García")
.font(.title.bold())
Text("Desarrolladora iOS Senior")
.font(.subheadline)
.foregroundStyle(.secondary)
// Estadísticas
HStack(spacing: 32) {
estadistica(valor: "142", etiqueta: "Proyectos")
estadistica(valor: "3.8K", etiqueta: "Seguidores")
estadistica(valor: "89", etiqueta: "Artículos")
}
.padding(.vertical)
// Contenido del perfil
ForEach(0..<10) { i in
RoundedRectangle(cornerRadius: 12)
.fill(.gray.opacity(0.1))
.frame(height: 80)
.overlay(Text("Proyecto \(i + 1)"))
}
}
.padding()
}
// Capa de navegación flotante (Liquid Glass)
VStack {
Spacer()
GlassEffectContainer(spacing: 12) {
HStack(spacing: 16) {
if mostrarOpciones {
Button("Mensaje", systemImage: "message") { }
.glassEffect(.regular.interactive())
.glassEffectID("mensaje", in: transicion)
Button("Seguir", systemImage: "person.badge.plus") { }
.glassEffect(
.regular.tint(.blue).interactive()
)
.glassEffectID("seguir", in: transicion)
}
Button {
withAnimation(.spring(
duration: 0.5, bounce: 0.25
)) {
mostrarOpciones.toggle()
}
} label: {
Image(systemName: mostrarOpciones
? "xmark" : "ellipsis")
.font(.title3)
.frame(width: 50, height: 50)
.contentTransition(
.symbolEffect(.replace)
)
}
.buttonStyle(.glassProminent)
.tint(.purple)
.glassEffectID("menu", in: transicion)
}
.padding(.bottom, 24)
}
}
}
}
private func estadistica(
valor: String, etiqueta: String
) -> some View {
VStack(spacing: 4) {
Text(valor)
.font(.title2.bold())
Text(etiqueta)
.font(.caption)
.foregroundStyle(.secondary)
}
}
}
Este ejemplo junta todos los patrones clave: contenido sin cristal en la capa principal, controles flotantes con GlassEffectContainer, transiciones morfológicas con glassEffectID, tintes semánticos diferenciados (azul para seguir, morado para el menú), y modo interactivo en todos los botones. Si entiendes este ejemplo, ya tienes las bases para cualquier interfaz con Liquid Glass.
Preguntas Frecuentes
¿Necesito reescribir mi app para usar Liquid Glass?
No, para nada. Si tu app usa componentes estándar de SwiftUI o UIKit (como NavigationStack, TabView, .toolbar), al recompilar con Xcode 26 obtienes Liquid Glass automáticamente. Solo necesitas código nuevo si quieres aplicar el efecto a vistas personalizadas.
¿Puedo desactivar Liquid Glass si mi app no está lista?
Sí, puedes. Apple proporciona la clave UIDesignRequiresCompatability en Info.plist (tipo Boolean, valor YES) para desactivar el efecto. Pero ojo, esta opción es temporal y solo estará disponible hasta la próxima versión mayor de iOS.
¿Qué pasa con la legibilidad del texto sobre Liquid Glass?
Apple ha integrado adaptaciones de accesibilidad directamente en el material. Los ajustes del sistema como Reducir Transparencia, Aumentar Contraste y Reducir Movimiento modifican automáticamente la apariencia del cristal. No necesitas escribir código adicional — todo viene de serie.
¿Liquid Glass funciona en versiones anteriores de iOS?
No. Las APIs de .glassEffect() requieren iOS 26 como mínimo. Para apps que necesitan soportar versiones anteriores, puedes crear modificadores condicionales con #available(iOS 26, *) que usen .ultraThinMaterial como fallback. Más arriba tienes un ejemplo de cómo hacerlo.
¿Cuál es la diferencia entre .glass y .glassProminent como estilos de botón?
.glass es translúcido y está pensado para acciones secundarias, mientras que .glassProminent es más opaco y tiene mayor presencia visual, reservado para la acción principal. Combina .tint() con .glassProminent para darle máximo énfasis a la acción más importante de la pantalla.