Implement Apple's Liquid Glass design patterns — A dynamic material that blurs the content behind it, reflects the colors and light of surrounding content, and responds to touch and pointer interactions. Covers integration with SwiftUI, UIKit, and WidgetKit.
When to Activate
- Build or update apps that adopt the new design language for iOS 26+
- Implement glass-style buttons, cards, toolbars, or containers
- Create morphing transitions between glass elements
- Apply Liquid Glass effects to widgets
- Migrate existing blur/material effects to the new Liquid Glass API
Core Patterns — SwiftUI
Basic Glass Effects
The simplest way to add a Liquid Glass effect to any view:
Text("Hello, World!")
.font(.title)
.padding()
.glassEffect() // 默认:常规变体,胶囊形状
Custom Shapes and Tint
Text("Hello, World!")
.font(.title)
.padding()
.glassEffect(.regular.tint(.orange).interactive(), in: .rect(cornerRadius: 16.0))
Key customization options:
.regular— Standard glass effect.tint(Color)— Add color tint to highlight.interactive()— Respond to touch and pointer interactions- Shapes:
.capsule(default),.rect(cornerRadius:)、.circle
Glass Button Styles
Button("Click Me") { /* 操作 */ }
.buttonStyle(.glass)
Button("Important") { /* 操作 */ }
.buttonStyle(.glassProminent)
GlassEffectContainer for Multiple Elements
Always wrap multiple glass views in a container to optimize performance and enable morphing effects:
GlassEffectContainer(spacing: 40.0) {
HStack(spacing: 40.0) {
Image(systemName: "scribble.variable")
.frame(width: 80.0, height: 80.0)
.font(.system(size: 36))
.glassEffect()
Image(systemName: "eraser.fill")
.frame(width: 80.0, height: 80.0)
.font(.system(size: 36))
.glassEffect()
}
}
spacing The parameter controls the merge distance — the closer elements are, the more their glass shapes blend together.
Union Glass Effect
Use glassEffectUnion to merge multiple views into a single glass shape:
@Namespace private var namespace
GlassEffectContainer(spacing: 20.0) {
HStack(spacing: 20.0) {
ForEach(symbolSet.indices, id: \.self) { item in
Image(systemName: symbolSet[item])
.frame(width: 80.0, height: 80.0)
.glassEffect()
.glassEffectUnion(id: item < 2 ? "group1" : "group2", namespace: namespace)
}
}
}
Morphing Transitions
Create smooth morphing when glass elements appear/disappear:
@State private var isExpanded = false
@Namespace private var namespace
GlassEffectContainer(spacing: 40.0) {
HStack(spacing: 40.0) {
Image(systemName: "scribble.variable")
.frame(width: 80.0, height: 80.0)
.glassEffect()
.glassEffectID("pencil", in: namespace)
if isExpanded {
Image(systemName: "eraser.fill")
.frame(width: 80.0, height: 80.0)
.glassEffect()
.glassEffectID("eraser", in: namespace)
}
}
}
Button("Toggle") {
withAnimation { isExpanded.toggle() }
}
.buttonStyle(.glass)
Extending Horizontal Scrolling Under Sidebars
To allow horizontally scrolling content to extend under a sidebar or inspector, ensure the ScrollView content touches the container's leading/trailing edges. When the layout extends to the edges, the system automatically handles the scrolling behavior under the sidebar — no additional modifiers are needed.
Core Patterns — UIKit
Basic UIGlassEffect
let glassEffect = UIGlassEffect()
glassEffect.tintColor = UIColor.systemBlue.withAlphaComponent(0.3)
glassEffect.isInteractive = true
let visualEffectView = UIVisualEffectView(effect: glassEffect)
visualEffectView.translatesAutoresizingMaskIntoConstraints = false
visualEffectView.layer.cornerRadius = 20
visualEffectView.clipsToBounds = true
view.addSubview(visualEffectView)
NSLayoutConstraint.activate([
visualEffectView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
visualEffectView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
visualEffectView.widthAnchor.constraint(equalToConstant: 200),
visualEffectView.heightAnchor.constraint(equalToConstant: 120)
])
// 向 contentView 添加内容
let label = UILabel()
label.text = "Liquid Glass"
label.translatesAutoresizingMaskIntoConstraints = false
visualEffectView.contentView.addSubview(label)
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: visualEffectView.contentView.centerXAnchor),
label.centerYAnchor.constraint(equalTo: visualEffectView.contentView.centerYAnchor)
])
UIGlassContainerEffect for Multiple Elements
let containerEffect = UIGlassContainerEffect()
containerEffect.spacing = 40.0
let containerView = UIVisualEffectView(effect: containerEffect)
let firstGlass = UIVisualEffectView(effect: UIGlassEffect())
let secondGlass = UIVisualEffectView(effect: UIGlassEffect())
containerView.contentView.addSubview(firstGlass)
containerView.contentView.addSubview(secondGlass)
Scroll Edge Effects
scrollView.topEdgeEffect.style = .automatic
scrollView.bottomEdgeEffect.style = .hard
scrollView.leftEdgeEffect.isHidden = true
Toolbar Glass Integration
let favoriteButton = UIBarButtonItem(image: UIImage(systemName: "heart"), style: .plain, target: self, action: #selector(favoriteAction))
favoriteButton.hidesSharedBackground = true // 选择不使用共享玻璃背景
Core Patterns — WidgetKit
Rendering Mode Detection
struct MyWidgetView: View {
@Environment(\.widgetRenderingMode) var renderingMode
var body: some View {
if renderingMode == .accented {
// 强调色模式:白色色调、主题化的玻璃背景
} else {
// 全色模式:标准外观
}
}
}
Accent Groups for Visual Hierarchy
HStack {
VStack(alignment: .leading) {
Text("Title")
.widgetAccentable() // 强调组
Text("Subtitle")
// 主要组(默认)
}
Image(systemName: "star.fill")
.widgetAccentable() // 强调组
}
Image Rendering in Accented Mode
Image("myImage")
.widgetAccentedRenderingMode(.monochrome)
Container Backgrounds
VStack { /* 内容 */ }
.containerBackground(for: .widget) {
Color.blue.opacity(0.2)
}
Key Design Decisions
| Decision | Rationale |
|---|---|
| Wrap with GlassEffectContainer | Performance optimization, supports morphing transitions between glass elements |
spacing parameter |
Controls the merge distance — fine-tune how close elements must be to blend together |
@Namespace + glassEffectID |
Supports smooth morphing transitions when the view hierarchy changes |
interactive() modifier |
Explicitly enable touch/pointer response — not all glass should be responsive |
| UIGlassContainerEffect in UIKit | Maintains a container pattern consistent with SwiftUI |
| Accent rendering mode in widgets | When the user selects an accent color Home Screen, the system applies tinted glass |
Best Practices
- Always use GlassEffectContainer: It enables morphing effects and improves rendering performance when applying glass effects to multiple sibling views.
- Apply after other appearance modifiers (frame, font, padding)
.glassEffect()。 - Use only on elements that respond to user interaction
.interactive()(such as buttons and toggleable items). - Choose container spacing carefully: to control when glass effects merge.
- Use
withAnimation: use it when changing the view hierarchy to enable smooth morphing transitions. - Test under different appearances: including light mode, dark mode, and accent color/tinted modes.
- Ensure accessibility contrast: text on glass must remain clearly legible.
Anti-patterns to avoid
- Using multiple independent
.glassEffect()views without a GlassEffectContainer. - Nesting too many glass effects: it degrades performance and visual clarity.
- Applying glass effects to every view: reserve it for interactive elements, toolbars, and cards.
- Forgetting to set
clipsToBounds = true。 - Ignoring accent rendering modes in widgets: this breaks the accent color Home Screen appearance.
- Using opaque backgrounds behind glass: this breaks the translucent effect.
When to Use
- Navigation bars, toolbars, and tab bars that adopt the new iOS 26 design.
- Floating action buttons and card-style containers.
- Interactive controls that need visual depth and touch feedback.
- Widgets that should integrate with the system's dynamic glass appearance.
- Morphing transitions between related UI states.