swiftui-liquid-glass
Implement, review, or improve SwiftUI Liquid Glass effects for iOS 26+. Covers glassEffect modifier, GlassEffectContainer, glass button styles, glas…
它会碰到什么
这一栏是扫描器报的事实,不是结论。命中多不等于有毒(安全工具、规则库、示例脚本本来就会包含危险写法),命中少也不等于干净。它和你手上的凭据、文件、网络有什么关系,需要你自己看。
技能内容
SwiftUI Liquid Glass
Liquid Glass is the dynamic translucent material introduced across Apple platforms
- Standard SwiftUI bars and presentations adopt it automatically when built with
the current SDK. Reserve custom glass for functional controls and navigation surfaces,
not general content backgrounds.
See [references/liquid-glass.md](references/liquid-glass.md) for the full API reference with additional examples.
Contents
- [Workflow](#workflow)
- [Core API Summary](#core-api-summary)
- [Code Examples](#code-examples)
- [Common Mistakes](#common-mistakes)
- [Review Checklist](#review-checklist)
- [References](#references)
Workflow
Choose the path that matches the request:
1. Implement a new feature with Liquid Glass
- Identify target surfaces (floating controls, custom bars, transient controls).
- Decide shape, prominence, and whether each element is a real control or static status.
- Wrap grouped glass elements in a
GlassEffectContainer. - Apply
.glassEffect()after layout and appearance modifiers. - Add
.interactive()only to tappable/focusable elements. - Add morphing transitions with
glassEffectID(_:in:)where the view hierarchy
changes with animation. Put glassEffectTransition(_:) on the inserted or
removed glass child, not on the always-present container, and choose the style
using [Morphing & Transitions](#morphing--transitions).
- Gate with
if #available(iOS 26, *)and provide a fallback for earlier versions.
2. Improve an existing feature with Liquid Glass
- Find custom control or navigation backgrounds that can be replaced with
.glassEffect(). - Wrap sibling glass elements in
GlassEffectContainerfor blending and performance. - Replace custom glass-like buttons with
.buttonStyle(.glass),.buttonStyle(.glassProminent), or configurable styles such as.buttonStyle(.glass(.clear)). Use.glass(_:)when the button needs a specific tint or variant; reserve.glassProminentfor high-emphasis primary actions. - Add morphing transitions where animated insertion/removal occurs.
3. Review existing Liquid Glass usage
Trace each effect through content/control ownership, layout order, container scope, interactivity, transitions, availability, accessibility settings, and fallback. Restore the same UI fixture and rerun the checklist after each correction.
Core API Summary
glassEffect(_:in:)
Applies Liquid Glass behind a view. Default: .regular variant in a Capsule shape.
nonisolated func glassEffect(
_ glass: Glass = .regular,
in shape: some Shape = DefaultGlassEffectShape()
) -> some View
Glass struct
| Property / Method | Purpose |
|---|---|
| .regular | Standard glass material |
| .clear | Clear variant; add dimming/contrast treatment when legibility needs it |
| .identity | No visual effect (pass-through) |
| .tint(_:) | Add a color tint for prominence |
| .interactive(_:) | React to touch and pointer interactions |
Chain them: .regular.tint(.blue).interactive()
GlassEffectContainer
Wraps multiple glass views for shared rendering, blending, and morphing.
GlassEffectContainer(spacing: 24) {
// child views with .glassEffect()
}
The spacing controls when nearby glass shapes begin to blend. Match or exceed
the interior layout spacing so shapes merge during animated transitions but remain
separate at rest.
Morphing & Transitions
| Modifier | Purpose |
|---|---|
| glassEffectID(_:in:) | Stable identity for morphing during view hierarchy changes |
| glassEffectUnion(id:namespace:) | Merge multiple views into one glass shape |
| glassEffectTransition(_:) | Control how glass appears/disappears |
Transition decision: use .matchedGeometry for nearby effects inside the container's
spacing; use .materialize for distant insertion/removal or when no geometry match
should occur; use .identity only when no transition animation is wanted.
Button Styles
Button("Action") { }
.buttonStyle(.glass) // standard glass button
Button("Primary") { }
.buttonStyle(.glassProminent) // prominent glass button
Button("Media") { }
.buttonStyle(.glass(.clear)) // configurable variant; verify contrast
Related iOS 26 APIs
| API | Use |
|---|---|
| scrollEdgeEffectStyle | Configure a scroll boundary's visual treatment. |
| backgroundExtensionEffect | Extend one background under safe-area edges with mirrored blur. |
| ToolbarSpacer | Create a visual break between toolbar items. |
See the corresponding sections in
[references/liquid-glass.md](references/liquid-glass.md) for signatures and examples.
Code Examples
Glass button with availability gate
if #available(iOS 26, *) {
Button("Show Status") { showStatusDetails() }
.buttonStyle(.glass)
} else {
Button("Show Status") { showStatusDetails() }
.buttonStyle(.bordered)
}
Grouped glass shapes in a container
let symbols = ["pencil", "eraser.fill", "lasso"]
GlassEffectContainer(spacing: 24) {
HStack(spacing: 24) {
ForEach(symbols, id: \.self) { symbol in
Image(systemName: symbol)
.frame(width: 56, height: 56)
.glassEffect()
}
}
}
Nearby morphing transition
@State private var isExpanded = false
@Namespace private var ns
var body: some View {
GlassEffectContainer(spacing: 40) {
HStack(spacing: 40) {
Image(systemName: "pencil")
.frame(width: 80, height: 80)
.glassEffect()
.glassEffectID("pencil", in: ns)
if isExpanded {
Image(systemName: "eraser.fill")
.frame(width: 80, height: 80)
.glassEffect()
.glassEffectID("eraser", in: ns)
.glassEffectTransition(.matchedGeometry)
}
}
}
Button("Toggle") {
withAnimation { isExpanded.toggle() }
}
.buttonStyle(.glass)
}
Unioning views into a single glass shape
@Namespace private var ns
GlassEffectContainer(spacing: 20) {
HStack(spacing: 20) {
ForEach(items.indices, id: \.self) { i in
Image(systemName: items[i])
.frame(width: 80, height: 80)
.glassEffect()
.glassEffectUnion(id: i < 2 ? "group1" : "group2", namespace: ns)
}
}
}
Tinted glass icon control
struct GlassIconControl: View {
let icon: String
let tint: Color
let action: () -> Void
var body: some View {
Button(action: action) {
Image(systemName: icon)
.font(.title2)
.padding()
}
.buttonStyle(.glass(.regular.tint(tint)))
}
}
Clear glass action over bright content
if #available(iOS 26, *) {
ZStack {
Capsule()
.fill(.black.opacity(0.28))
Button {
playRecap()
} label: {
Label("Play", systemImage: "play.fill")
.font(.headline)
.padding(.horizontal, 8)
}
.buttonStyle(.glass(.clear))
}
.fixedSize()
} else {
Button("Play", systemImage: "play.fill") { playRecap() }
.buttonStyle(.borderedProminent)
}
Use clear glass only when the background still leaves labels and symbols readable.
On bright or busy backgrounds, add a subtle dimming layer or choose a more opaque
button style.
Static status count, not a toolbar control
VStack(spacing: 8) {
Text("24")
.font(.headline.monospacedDigit())
.accessibilityLabel("24 options expiring")
Text("Options Expiring")
.font(.subheadline)
.foregroundStyle(.secondary)
}
// Keep read-only status out of toolbar control slots and do not add .interactive().
Common Mistakes
| Mistake | Fix |
|---|---|
| Glass decorates static content | Keep it in the controls/navigation layer. |
| Read-only status uses .interactive() or an action slot | Present status as content, or make the whole badge one real accessible action. |
| Related effects use nested containers | Use one GlassEffectContainer per related blending/morphing group. |
| .glassEffect() precedes padding/frame/style | Apply layout and appearance first, then glass. |
| Custom effects assume default accessibility settings | Test Reduce Transparency, Reduce Motion, contrast, and legibility. |
| No pre-iOS 26 path | Gate the effect and preserve a functional fallback. |
Review Checklist
- [ ] Availability:
if #available(iOS 26, *)present with fallback UI. - [ ] Container: Multiple glass views wrapped in
GlassEffectContainer. - [ ] Modifier order:
.glassEffect()applied after layout/appearance modifiers. - [ ] Interactivity:
.interactive()used only where user interaction exists. - [ ] Status vs action: Static counts/status are not toolbar controls and do not expose press/hover affordance.
- [ ] Transitions:
glassEffectIDused with@Namespacefor morphing animations. - [ ] Transition type:
.matchedGeometryfor nearby effects;.materializefor distant ones, applied to the conditional glass child. - [ ] Consistency: Shapes, tints, and spacing are uniform across related elements.
- [ ] Performance: Glass effects are limited in number; container used for grouping.
- [ ] Accessibility: Tested with Reduce Transparency and Reduce Motion enabled.
- [ ] Button styles: Standard
.glass,.glassProminent, or configurable.glass(_:)used for buttons;.glass(_:)is primary when a tint or clear variant is required. - [ ] Clear glass contrast: Clear glass over bright content has a dimming/contrast treatment or uses a more legible style.
- [ ] Concurrency: IDs passed to
glassEffectID/glassEffectUnionareSendable; MainActor-annotated Liquid Glass APIs stay in SwiftUI UI code.
References
- Full API guide: [references/liquid-glass.md](references/liquid-glass.md)
- Apple docs: Applying Liquid Glass to custom views
- Apple docs: Adopting Liquid Glass
想直接用这个技能?
本站把开放许可(MIT / Apache 等)的技能按仓库打包整理到网盘,点一下转存到你自己的网盘,不用一个个从 GitHub 拉。许可未声明的技能只给原始仓库链接,不打包。