This is the first article in a multi-part series diving into Apple’s new Liquid Glass design language introduced in iOS 26. In this post, we’ll cover the basics and how to apply .glassEffect()
across common SwiftUI views. Future articles will explore advanced custom containers, UIKit implementation, and new tab/navigation structures.
this post is beginner-friendly but expects iOS26 + Xcode 26 beta
🔍 Upcoming in this series:
- Part 2: Deep Dive on Tab Bars, Navigation, and Sidebars
- Part 3: Creating Custom Containers with
.glassEffect()
- Part 4: Implementing Liquid Glass in UIKit
Whether you’re designing a modern SwiftUI app or just experimenting with iOS 26, this guide will give you a strong foundation for working with the new visual paradigm.
Table Of Contents
- Introduction
- Tools and Setup
- SwiftUI Examples with Glass Effect
- Observations, Quirks, and Gotchas
- What's Next
- Resources
1. Brief introduction to Liquid Glass design—what it is and why it matters
I am assuming that you are aware of the announcement from WWDC25, where Apple made their broadest redesign, and presented "Liquid Glass".
Apple Developer documentation overview states:
Interfaces across Apple platforms feature a new dynamic material called Liquid Glass, which combines the optical properties of glass with a sense of fluidity.
Here are some videos for some refresher. I suggest you watch these before we dive in. 🤿
2. Tools and Setup
What you need.
- Xcode 26 (as of writing, we are using the beta)
- Make sure the project's minimum target is iOS26
Random Example - where I compiled my app in Xcode26. see the Tab bar.
3. SwiftUI Examples with .glassEffect()
Applying Glass Effect on Containers VStack and HStack
VStack {
Text("Large Title")
.font(.largeTitle)
Text("Subheadline")
.font(.subheadline)
Text("body")
.font(.body)
}
.glassEffect()
HStack {
Text("Large Title")
.font(.largeTitle)
Text("Subheadline")
.font(.subheadline)
Text("body")
.font(.body)
}
.glassEffect()
Applying Glass Effect on GroupBox
note that the padding inside the content is automatically applied
GroupBox {
HStack {
Text("Large Title")
.font(.largeTitle)
Text("Subheadline")
.font(.subheadline)
Text("body")
.font(.body)
}
}
.glassEffect()
It looks like it has No effect, but if we apply .backgroundStyle(Color.red) , we can see that they have overlapped with each other.
.glassEffect()
.backgroundStyle(Color.red)
so it might be considered a workaround if we set .backgroundStyle(Color.clear)
to the GroupBox
Applying Glass Effect on Forms
No effect when applying to Form container
Form {
Section("section 1") {
TextField("Enter text", text: $text)
TextField("Enter text", text: $text2)
}
Section("section 2") {
TextField("Enter text", text: $text)
.padding()
TextField("Enter text", text: $text2)
.padding()
}
}
.glassEffect()
Form {
Section("section 1") {
TextField("Enter text", text: $text)
TextField("Enter text", text: $text2)
}
Section("section 2") {
TextField("Enter text", text: $text)
.padding()
TextField("Enter text", text: $text2)
.padding()
}
}
.glassEffect()
only applies to the inner child views when attached in Section
Section("section 2") {
TextField("Enter text", text: $text)
.padding()
TextField("Enter text", text: $text2)
.padding()
}
.glassEffect()
Button with buttonStyle Glass
Button {
//action
} label: {
Text("Glass Button")
}
.buttonStyle(.glass)
Tinted
Button {
//Action
} label: {
Text("Glass Button")
}
.buttonStyle(.glass)
.tint(.red)
Different Button Shapes
Toggles
Toggle toggleStyle(.button)
Toggle(isOn: $toggleValue1) {
Text("Toggle - Button Style")
}
.toggleStyle(.button)
.glassEffect()
Toggle in GroupBox Container
GroupBox {
Toggle(isOn: $toggleValue1) {
Text("Toggle")
}
.labelsHidden()
.tint(.orange)
}
Toggle with Gradient Background Style
Toggle(isOn: $toggleValue1) {
Text("Toggle")
}
.labelsHidden()
.padding(32)
.background {
RoundedRectangle(cornerRadius: 25)
.fill(
RadialGradient(
colors: [Color.green, Color.blue, Color.teal],
center: .bottomLeading,
startRadius: 50,
endRadius: 300
)
)
}
.tint(Color.red)
Toggle with Material Backgrounds
Toggle(isOn: $toggleValue1) {
Text("Toggle")
}
.labelsHidden()
.padding(32)
.background {
RoundedRectangle(cornerRadius: 25)
.fill(
Material.ultraThin
)
}
Slider
Slider - with labels
Slider(
value: $sliderValue,
in: 0...100,
label: {
Label("Label", systemImage: "smartphone")
},
minimumValueLabel: {
Text("Minimum Label")
},
maximumValueLabel: {
Text("Maximum Label")
}
)
Sliders embedded in a GroupBox
GroupBox {
Slider(value: $sliderValue, in: 0...100)
}
.backgroundStyle(Material.ultraThin)
GroupBox {
Slider(value: $sliderValue, in: 0...100)
.padding()
}
.backgroundStyle(
LinearGradient(
colors: [Color.red, Color.orange, Color.teal],
startPoint: .leading,
endPoint: .trailing
)
)
.tint(.yellow)
Slider - with stepper
Slider(value: $sliderValue, in: 0...100, step: 10)
Picker
with .glassEffect
Modifier
Picker(selection: $selectedRegion) {
Text("NCR").tag(String("NCR"))
Text("Ilocos Region").tag(String("Ilocos Region"))
Text("Central Luzon").tag(String("Central Luzon"))
Text("SOCCSKSARGEN").tag(String("SOCCSKSARGEN"))
Text("BARMM").tag(String("BARMM"))
} label: {
Text("Select")
}
.glassEffect()
.tint(.white)
without the .glassEffect
Modifier, the picker button does not have the glass effect, or embedded in a glass container
Picker(selection: $selectedRegion) {
Text("NCR").tag(String("NCR"))
Text("Ilocos Region").tag(String("Ilocos Region"))
Text("Central Luzon").tag(String("Central Luzon"))
Text("SOCCSKSARGEN").tag(String("SOCCSKSARGEN"))
Text("BARMM").tag(String("BARMM"))
} label: {
Text("Select")
}
.tint(.white)
Segmented Picker style
with .glassEffect()
modifier
Picker(selection: $selectedRegion) {
Text("NCR").tag(String("NCR"))
Text("Ilocos Region").tag(String("Ilocos Region"))
Text("Central Luzon").tag(String("Central Luzon"))
Text("SOCCSKSARGEN").tag(String("SOCCSKSARGEN"))
Text("BARMM").tag(String("BARMM"))
} label: {
Text("Select")
}
.glassEffect()
.pickerStyle(.segmented)
without the .glassEffect()
modifier
Picker(selection: $selectedRegion) {
Text("NCR").tag(String("NCR"))
Text("Ilocos Region").tag(String("Ilocos Region"))
Text("Central Luzon").tag(String("Central Luzon"))
Text("SOCCSKSARGEN").tag(String("SOCCSKSARGEN"))
Text("BARMM").tag(String("BARMM"))
} label: {
Text("Select")
}
.pickerStyle(.segmented)
Menu Picker Style
With image background
Without image background
Picker(selection: $selectedRegion) {
Text("NCR").tag(String("NCR"))
Text("Ilocos Region").tag(String("Ilocos Region"))
Text("Central Luzon").tag(String("Central Luzon"))
Text("SOCCSKSARGEN").tag(String("SOCCSKSARGEN"))
Text("BARMM").tag(String("BARMM"))
Picker("Sub Picker", selection: $selectedRegion) {
Text("Item 1").tag(String("Item 1"))
Text("Item 2").tag(String("Item 2"))
Menu {
Text("Menu").tag(String("Menu"))
} label: {
Text("Menu Item")
}
}
} label: {
Text("Select")
}
.pickerStyle(.menu)
Picker containing a child Picker with Palette style
Menu
Menu {
ForEach(Option.allCases, id: \.self) { option in
Button {
selectedOption = option
} label: {
Label(
option.displayTitle,
systemImage: option.imageSystemName
)
}
}
Menu {
ForEach(Vehicle.allCases, id: \.self) { option in
Button {
selectedSub = option
} label: {
Label(
option.displayTitle,
systemImage: option.imageSystemName
)
}
}
} label: {
Label("Submenu", systemImage: "car")
}
} label: {
Text("Menu")
}
.foregroundStyle(.primary, .red)
.padding()
.glassEffect()
Date Picker
Apply glassEffect modifier
VStack {
GroupBox {
Text("With Glass effect")
}
DatePicker(
"Date Picker",
selection: $selectedDate,
displayedComponents: [.hourAndMinute, .date]
)
.padding(.horizontal)
.glassEffect()
.padding(.horizontal)
}
Apply glass effect on the graphical style? hmm
VStack {
GroupBox {
Text("Graphical date picker style + Glass Effect")
}
GlassEffectContainer {
DatePicker(
"Date Picker",
selection: $selectedDate,
displayedComponents: [.hourAndMinute, .date]
)
.padding()
.datePickerStyle(.graphical)
.glassEffect()
}
.padding()
}
Color Picker
We can apply .glassEffect()
modifier to the Color picker container.
The Liquid Glass style on Sliders are automatically applied on iOS26.
Gauge
Apply glassEffect modifier to Gauge with different gauge styles
VStack(spacing: 64) {
Gauge(value: 0.5, label: { Text("50%") })
.padding()
.glassEffect()
Gauge(value: 0.2, label: { Text("20%") })
.padding()
.glassEffect()
.gaugeStyle(.accessoryCircular)
Gauge(value: 0.8, label: { Text("80%") })
.padding()
.glassEffect()
.gaugeStyle(.accessoryCircularCapacity)
Gauge(value: 0.7, label: { Text("70%") })
.padding()
.glassEffect()
.gaugeStyle(.accessoryLinear)
Gauge(value: 0.8, label: { Text("80%") })
.padding()
.glassEffect()
.gaugeStyle(.accessoryLinearCapacity)
}
.padding(.horizontal)
Progress View
ProgressView("Progress", value: 50, total: 100)
.padding()
.glassEffect()
ProgressView("Progress", value: 50, total: 100)
.padding()
.glassEffect()
.progressViewStyle(.circular)
ProgressView(value: 50, total: 100)
.padding()
.glassEffect()
.progressViewStyle(.circular)
Stepper
Adding glassEffect to Stepper
Stepper(value: $stepperValue.animation(.bouncy), in: 0...100) {
Text("\(stepperValue)")
.font(.title.bold())
.contentTransition(.numericText())
}
.padding()
.glassEffect()
TextField
Apply .glassEffect()
on TextField
TextField("Textfield", text: $textFieldValue)
.padding()
.glassEffect()
.glassEffect()
, TextFieldStyle - roundedBorder
TextField("Textfield", text: $textFieldValue)
.padding()
.glassEffect()
.textFieldStyle(.roundedBorder)
.glassEffect(), TextFieldStyle - roundedBorder, and axis is vertical
`.glassEffect()`, TextFieldStyle - roundedBorder, and axis is vertical
Apply glassEffect on TextField, with axis set on vertical
TextField("Textfield", text: $textFieldValue, axis: .vertical)
.padding()
.glassEffect()
Presentations and Overlays
Popovers on small screen
Note that popovers appears as modals when on compact screen, on regular screens like iPad, it will look more a popover
Popovers on larger screen
.popover(isPresented: $popoverIsPresented, content: {
Text("Popover Content")
.padding()
.presentationDetents([.height(100), .medium, .large])
})
Sheet
.sheet(isPresented: $presentSheet) {
Text("Sheet Content")
.presentationDetents([.height(100), .medium, .large])
}
Action Sheet / Confirmation Dialog
.confirmationDialog(
"Confirmation",
isPresented: $showActionSheet) {
Button("OK") {
}
Button("Destructive", role: .destructive) {
}
Button("Close", role: .close) {
}
Button("Confirm", role: .confirm) {
}
}
Alert
.alert("Alert", isPresented: $showAlert) {
Button("OK", role: .cancel) {
}
Button("Destructive", role: .destructive) {
}
Button("Close", role: .close) {
}
Button("Confirm", role: .confirm) {
}
Button("Button") {
}
} message: {
Text("Some Message Texts")
}
Tab bar
Tab
Tab with Role - search
4. Observations, Quirks, and Gotchas
Native Navigation Back button - don’t have title of the previous screen
Retained behavior: when long pressing the back button shows list of previous screens
Customizing the Navigation back button
using toolbar does not work, and cannot override even using .labelsVisibility(.visible)
, the button
struct DestinationNormalView: View {
var body: some View {
VStack {
Text("DestinationNormalView")
.navigationBarBackButtonHidden()
.toolbar {
ToolbarItem(
id: "back-button",
placement: .topBarLeading) {
Button {
} label: {
Label("Back", systemImage: "chevron.left")
.labelsVisibility(.visible)
}
}
}
}
}
}
Alternatively, you can customize your own back, though you will lose the ability to do the long-press gesture showing the previous screens.
Approach 1.
var body: some View {
ZStack {
VStack {
Text("DestinationNormalView")
}
VStack {
HStack(spacing: 4) {
Button {
presentationMode.wrappedValue.dismiss()
} label: {
Label("Back", systemImage: "chevron.left")
}
Spacer()
}
.padding(.horizontal)
Spacer()
}
.navigationBarBackButtonHidden()
}
}
Approach 2.
var body: some View {
ZStack {
Color.white
VStack {
Text("DestinationUsingOverlayView")
}
}
.ignoresSafeArea(.all)
.background(Color.blue)
.overlay(alignment: .top) {
HStack(spacing: 4) {
Button {
presentationMode.wrappedValue.dismiss()
} label: {
Label("Back", systemImage: "chevron.left")
}
Spacer()
}
.padding(.horizontal)
}
.navigationBarBackButtonHidden()
}
📘 What’s Next?
This is just the beginning of our journey into Liquid Glass and the new UI possibilities in iOS 26.
In the next article, we’ll take a closer look at how system components like Tab Bars, Navigation, and Sidebars behave under Liquid Glass—and how you can customize or override them using SwiftUI.
If you found this helpful, consider following me here on Dev.to — and feel free to comment below with your thoughts, issues, or requests for what you’d like to see next.
Thanks for reading, and happy coding! 💧💎
Resources
Adopting Liquid Glass
Sample Code: Landmarks: Building an app with Liquid Glass
Top comments (0)