You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
GlassBottomAccessory component wrapping tabViewBottomAccessory: a persistent glass control (e.g. a "now playing" pill) docked above the tab bar that survives navigation. Forwards to the system accessory slot on iOS 26+ (handling the isEnabled: overload's split availability — it ships in iOS 26.1, one point release after the base overload). iOS 17–18 (and any non-iOS platform, where the system API doesn't exist) render a floating glass pill via safeAreaInset(edge: .bottom), wrapped in its own GlassEffectContainer. Takes a GlassBottomAccessoryHost (.tabView or .customTabBar) so it composes correctly whether the screen uses a real TabView or GlassTabBar. content is wrapped by the exact same modifier structure regardless of isEnabled, so toggling it at runtime only animates the accessory pill in/out — it never tears down and rebuilds content (which would otherwise lose @State, TabView selection, navigation stack, or scroll position inside it).
.glassMorphUnion(id:in:style:tint:cornerRadius:) view modifier wrapping glassEffectUnion, named to avoid shadowing the system API (same precedent as glassMorphID). iOS 17–18 have no union primitive, so rather than fake it with matchedGeometryEffect (which animates position, not a shared-shape material sample — a mistake seen in real adopters), the fallback uses an anchor-preference approach: participants report their frame, and the enclosing GlassEffectContainer draws one shared fallback-material surface behind the whole group, with individual participant backgrounds suppressed so nothing double-renders. A participant used outside any GlassEffectContainer has nowhere to report that frame to, so it degrades to a plain, unmerged .glass(...) surface instead of silently rendering nothing (see the "Fixed" entry below). Kill-switched on iOS 26 too, for now — gated behind the new GlassMaterial.nativeGlassMorphingEnabled flag (false by default; see the "Fixed" entry below), the same flag GlassEffectContainer/glassMorphID use, since the native glassEffect + glassEffectUnion forward shares the rendering pipeline implicated in that prior device-only corruption bug and hasn't been verified safe on-device.
.glassBackgroundExtension(isEnabled:) view modifier wrapping backgroundExtensionEffect, for extending a background layer (e.g. a hero image) under floating glass chrome instead of getting hard-clipped by it. Forwards directly on iOS 26+. iOS 17–18 approximate it by applying .ignoresSafeArea() directly to the extended content (instantiated exactly once — see the "Fixed" entry below) and overlaying a gradient-masked feather scrim at the top and bottom edges; Reduce Transparency swaps the blurred scrim for a solid, fully opaque one. Documented as a background-layer-only exception to this package's navigation-layer-only glass rule.
Fixed
Docs/LiquidGlassAPIReference.md: corrected TabViewBottomAccessoryPlacement's cases to the confirmed .inline / .expanded (was previously listed as .expanded / .collapsed), and documented the split availability between tabViewBottomAccessory(content:) (iOS 26.0) and tabViewBottomAccessory(isEnabled:content:) (iOS 26.1), verified live against the iOS 26.4 SDK symbol index.
.glassMorphUnion(id:in:style:tint:cornerRadius:): on the fallback path, a participant with a non-nil id used outside a GlassEffectContainer used to suppress its own background and report an anchor preference that nothing would ever collect, rendering no glass at all — contradicting this modifier's own doc comment that "this modifier applies the glass itself." GlassEffectContainer's fallback now marks content as inside a reducer via an internal \.isInsideGlassMorphUnionReducer environment flag; a participant that doesn't see that flag set renders as plain, unmerged glass instead.
iOS 17–18 fallback now honors Increased Contrast (colorSchemeContrast), closing a gap against this package's own accessibility rule: the native iOS 26 path already gets it from the system, but the fallback path never read it. GlassMaterial.borderOpacity(reduceTransparency:contrast:boost:) and the new GlassMaterial.borderLineWidth(contrast:) widen and brighten the fallback rim under Increased Contrast (independently of, and combinable with, Reduce Transparency), and flow through GlassRenderingModifier to every component built on .glass(...) (GlassButton, GlassCard, GlassTabBar, GlassBottomAccessory). GlassMorphUnionSurfaces's own fallback stroke (drawn outside GlassRenderingModifier) now reads the same environment value and routes through the same central computations. GlassBackgroundExtensionMetrics gains an independent contrast input that halves the bleed layer's blur and raises its opacity when Increased Contrast is on without Reduce Transparency (Reduce Transparency alone already maxes out both, so combining the two adds nothing further there).
GlassBottomAccessory: toggling isEnabled at runtime used to wrap content in structurally different branches (if isEnabled { content.someModifier{...} } else { content }), so SwiftUI treated content as having different identity per branch and tore it down/rebuilt it on every toggle — losing @State, TabView selection, navigation stack, and scroll position inside it, and dropping the intended appear/disappear transition in the process (it lived inside the now-discarded branch). content is now wrapped by the same modifier structure on every render; only the accessory subtree itself conditionally appears/disappears.
.glassBackgroundExtension(isEnabled:): the iOS 17–18 fallback used to instantiate content twice — once as the real view, once (scaled/blurred) inside its .background. For the documented AsyncImage hero-image use case this meant two independent fetches/decodes, and any side-effecting modifier inside content fired twice. content is now instantiated exactly once; the bleed effect comes from .ignoresSafeArea() on that single instance plus a feathered edge scrim that samples no content pixels.
Native SwiftUI.GlassEffectContainer / glassEffectID / glassEffectUnion forwarding was previously disabled by deleting each call site's native branch independently (GlassEffectContainer.body, glassMorphID, GlassMorphUnionModifier.body), connected only by prose doc comments — a partial re-enable (restoring one site but not another) could have silently reintroduced the iOS 26.5 rendering-corruption bug the kill switch exists to prevent. All three now gate entry into a live, compiled, type-checked native branch behind one shared flag, GlassMaterial.nativeGlassMorphingEnabled (Sources/LiquidGlass/GlassMaterial.swift, defaults to false), so re-enabling is "flip the flag" plus an on-device verification pass, not git archaeology. Also corrects GlassEffectContainer.swift's stale doc comment, which claimed glassMorphUnion forwards to the system glassEffectUnion on iOS 26 (it didn't, on any OS, at the time that comment was written).
GlassMorphUnionSurfaces.surface(for:) hand-reimplemented GlassRenderingModifier's fallback fill-selection and border-color logic, including a verbatim copy of its border-color computation. Both now route through shared GlassMaterial.fallbackFill(reduceTransparency:) and GlassMaterial.borderColor(reduceTransparency:colorScheme:) methods.
Removed GlassMaterial.borderOpacity(reduceTransparency:contrast:boost:contrastBoost:)'s unused contrastBoost parameter (never called with a non-default value); the function now always uses GlassMaterial.increasedContrastBorderBoost internally. New signature: borderOpacity(reduceTransparency:contrast:boost:).
GlassMorphUnionSurfaces recomputed its grouped union surfaces on every GeometryReader layout pass regardless of whether the resolved entries actually changed. It now caches the last resolved entries and their grouped result, skipping GlassMorphUnionReducer.group(...) when nothing moved.