Glass effect modifiers that back-deploy SwiftUI's glassEffect API while preserving native behaviour on OS 26+.
import UniversalGlass
Text("Hello")
.universalGlassEffect(.regular.tint(.purple))All overloads live in UniversalGlassEffect+View.swift:
View.universalGlassEffect(rendering:)
View.universalGlassEffect(_:rendering:)
View.universalGlassEffect(_:in:rendering:)
View.universalGlassEffect(in:rendering:)The overloads accept an optional UniversalGlass configuration and a UniversalGlassRendering toggle. UniversalGlass bundles the fallback Material with an optional Glass value for platforms that support it.
Each modifier performs the same availability dance:
- When the OS ships the native API and the caller picked
.automaticor.glass, SwiftUI's realglassEffectis invoked - When the caller forces
.material—or the OS lacksglassEffect—the view flows throughUniversalGlassEffectModifierinstead
UniversalGlassEffectModifier handles the back-port:
- Detects whether the view lives inside
UniversalGlassEffectContainer; if not, it renders its own background plate viaUniversalGlassFallbackBackground - Registers itself as a
GlassEffectParticipantusing an anchor preference, capturing geometry, the requested shape, and any union/ID metadata - Applies a
.transition(.blur)so insert/remove animations stay soft when native transitions are unavailable
UniversalGlassFallbackBackground paints the chosen Material and a shadow(.drop) to add depth.
UniversalGlassConfiguration exposes convenience cases that mirror Apple's material tiers:
| Configuration | Description |
|---|---|
.identity |
No effect |
.ultraThin |
Clear liquid glass over .ultraThinMaterial |
.thin |
Clear glass tinted slightly toward the platform background |
.regular |
Maps to Glass.regular |
.thick |
Deeper background tint alongside .thickMaterial |
.ultraThick |
Heavier tint to emulate Apple's deeper plates |
.clear |
Clear glass with ultra thin material fallback |
.universalGlassEffect(.regular.tint(.cyan)).universalGlassEffect(.regular.interactive())Override the material and tint used on older OS versions:
// Custom fallback material
.universalGlassEffect(.regular.fallback(material: .thin))
// Custom fallback tint
.universalGlassEffect(.thick.fallback(material: .regular, tint: .blue.opacity(0.2)))
// Remove fallback tint
.universalGlassEffect(.thick.fallback(tint: nil))// Custom shadow
.universalGlassEffect(
.regular.shadow(UniversalGlassShadow(color: .red, radius: 12))
)
// No shadow
.universalGlassEffect(.regular.shadow(.none))
// Default shadow (automatically applied)
.universalGlassEffect(.regular.shadow(.default))Shadow presets:
.default– Subtle black blur (8pt radius, 0.04 opacity).none– No shadow
Two environment-driven helpers coordinate multiple views:
View.universalGlassEffectUnion(id:namespace:rendering:)
View.universalGlassEffectID(_:in:rendering:)On OS 26+ these forward to SwiftUI. On earlier systems they stash metadata in glassEffectParticipantContext. The fallback container consumes this context to merge participants, creating a single slab of glass that "melts" neighbouring elements together.
View.universalGlassEffectTransition(_:) mirrors SwiftUI's transitions:
.materializeand.matchedGeometrymap to.transition(.fallbackBlur)on older systems.identityis a no-op- Once the deployment target hits OS 26, the helper forwards directly to the platform transition APIs
Custom transition per effect:
VStack {
Text("Slide Transition")
.universalGlassEffect()
.universalGlassTransition(.slide)
Text("Scale Transition")
.universalGlassEffect()
.universalGlassTransition(.scale)
Text("Blur Without Scale")
.universalGlassEffect()
.universalGlassTransition(.universalGlassMaterialBlurWithoutScale)
}Force all glass effects in a view hierarchy to use a specific rendering mode:
// Force material fallback (useful for testing)
MyApp()
.universalGlassRenderingMode(.material)
// Force glass when available
MyApp()
.universalGlassRenderingMode(.glass)All configuration methods are fully chainable:
Text("Fully Customized")
.universalGlassEffect(
.ultraThick
.fallback(material: .thin, tint: .cyan.opacity(0.3))
.shadow(UniversalGlassShadow(color: .blue, radius: 16))
.tint(.purple)
.interactive()
)
.universalGlassTransition(.opacity)- When using custom timing, reach for
AnyTransition.universalGlassMaterialBlur(intensity:scale:)and apply your animation on the parent view AnyTransition/universalGlassMaterialFallbackBlurexposes the same asymmetric animation UniversalGlass uses internally