Skip to content

Efecto Glass de iOS 26 con Compatibilidad Retroactiva en SwiftUI

Introducción

iOS 26 introdujo Liquid Glass, la evolución de diseño más significativa de Apple desde iOS 7. Este material translúcido y dinámico refleja y refracta el contenido circundante, creando un efecto visual sofisticado para elementos de navegación y controles.

Aunque el nuevo modificador .glassEffect() hace que implementar este diseño sea sencillo, la mayoría de las apps aún necesitan soportar versiones anteriores de iOS. Este artículo te muestra cómo crear una extensión de View reutilizable que usa Liquid Glass nativo en iOS 26+ y hace fallback a .thinMaterial en versiones anteriores.

El Desafío

Cuando intentas usar .glassEffect() en versiones de iOS anteriores a la 26, tu app no compila. Necesitas una solución que:

  1. Use Liquid Glass nativo en iOS 26+
  2. Proporcione un fallback consistente para iOS 18-25
  3. Mantenga tu código de vista limpio y simple

La Solución

Aquí hay una extensión de View que maneja ambos casos de forma elegante:

swift
import SwiftUI

extension View {
    /// Aplica un efecto glass con compatibilidad hacia adelante.
    /// - En iOS 26+: Usa `.glassEffect()` nativo para apariencia liquid glass
    /// - En iOS 18-25: Hace fallback a `.thinMaterial` para consistencia
    @ViewBuilder
    func glassedEffect(in shape: some Shape, interactive: Bool = false) -> some View {
        if #available(iOS 26.0, *) {
            self.glassEffect(interactive ? .regular.interactive() : .regular, in: shape)
                .clipShape(shape)
        } else {
            background {
                shape.fill(.thinMaterial)
            }
            .clipShape(shape)
        }
    }
}

Cómo Funciona

Analicemos los componentes clave:

1. El Atributo @ViewBuilder

El atributo @ViewBuilder permite que la función retorne diferentes tipos de vista dependiendo de la versión de iOS. Sin él, Swift requeriría que ambas ramas retornaran el mismo tipo.

2. Verificación de Disponibilidad

swift
if #available(iOS 26.0, *) {

Esta verificación en tiempo de compilación asegura que el código de Liquid Glass solo se ejecute en iOS 26+. El compilador sabe que esto es seguro y permite el modificador .glassEffect() en esa rama.

3. Liquid Glass Nativo (iOS 26+)

swift
self.glassEffect(interactive ? .regular.interactive() : .regular, in: shape)
    .clipShape(shape)

En iOS 26+, usamos el modificador nativo con:

  • Variante .regular para apariencia glass estándar
  • .interactive() para controles que deben responder al toque con efectos de escala y brillo
  • El parámetro shape controla el límite del glass

4. Fallback Material (iOS 18-25)

swift
background {
    shape.fill(.thinMaterial)
}
.clipShape(shape)

Para versiones anteriores, .thinMaterial proporciona un efecto translúcido similar que combina bien con el diseño del sistema.

Ejemplos de Uso

Botón Básico con Efecto Glass

swift
Button("Agregar Elemento") {
    // acción
}
.padding()
.glassedEffect(in: Capsule())

Botón de Acción Flotante Interactivo

swift
Button(action: agregarElemento) {
    HStack(spacing: 8) {
        Image(systemName: "plus")
            .font(.system(size: 20, weight: .semibold))
        Text("Agregar Nivel")
    }
    .foregroundStyle(Color.primary)
    .padding(.horizontal, 20)
    .frame(height: 58)
    .glassedEffect(in: Capsule(), interactive: true)
    .shadow(color: Color.black.opacity(0.12), radius: 12, y: 8)
}

Tarjeta con Esquinas Redondeadas

swift
VStack(alignment: .leading) {
    Text("Estadísticas")
        .font(.headline)
    Text("Tu resumen semanal")
        .font(.subheadline)
        .foregroundStyle(.secondary)
}
.padding()
.glassedEffect(in: RoundedRectangle(cornerRadius: 16))

Botón de Ícono Circular

swift
Button(action: mostrarConfiguracion) {
    Image(systemName: "gear")
        .font(.title2)
        .frame(width: 44, height: 44)
        .glassedEffect(in: Circle(), interactive: true)
}

Consideraciones de Diseño

Cuándo Usar Efectos Glass

Según las guías de diseño de Apple, Liquid Glass debe reservarse para la capa de navegación, no para el contenido principal. Piensa en toolbars, botones de acción flotantes y overlays de control.

Evita Anidar Glass

Glass no puede muestrear otros elementos glass. Si tienes múltiples elementos glass cerca unos de otros, envuélvelos en un GlassEffectContainer en iOS 26+ para asegurar un comportamiento visual consistente.

Extendiendo para Más Opciones

Puedes expandir la extensión para soportar más variantes de glass:

swift
extension View {
    @ViewBuilder
    func glassedEffect(
        in shape: some Shape,
        variant: GlassVariant = .regular,
        interactive: Bool = false
    ) -> some View {
        if #available(iOS 26.0, *) {
            let glass: Glass = switch variant {
            case .regular: .regular
            case .clear: .clear
            }
            self.glassEffect(interactive ? glass.interactive() : glass, in: shape)
                .clipShape(shape)
        } else {
            background {
                shape.fill(.thinMaterial)
            }
            .clipShape(shape)
        }
    }
}

enum GlassVariant {
    case regular
    case clear
}

Conclusión

Soportar el Liquid Glass de iOS 26 mientras se mantiene compatibilidad retroactiva no requiere abstracciones complejas. Una simple extensión de View con @ViewBuilder y verificaciones #available te da lo mejor de ambos mundos: Liquid Glass nativo en dispositivos más nuevos y un fallback elegante de material en dispositivos más antiguos.

Este patrón mantiene tu código de vista limpio—solo llama .glassedEffect(in: Capsule()) y deja que la extensión se encargue del resto.

Referencias