Liquid Glass is iOS 26’s new design language. Which means quite a lot of apps will probably be adopting a brand new UI philosophy that may require some vital adjustments to the way you’re designing your app’s UI.
In the event you’re not able to undertake Liquid Glass simply but, Apple has offered you an escape hatch that ought to be usable till the following main iOS launch.
I lately explored updating my exercise app Maxine to work effectively with Liquid Glass tab bars which you’ll study extra about right here.
On this put up, I’d prefer to discover how we will construct customized Liquid Glass elements for our apps operating on iOS 26 and its siblings. We’ll begin off by exploring when Liquid Glass is suitable after which transfer on to take a look at SwiftUI’s Liquid Glass associated view modifiers.
By the tip of this put up, we’ll have constructed the UI which you can see in motion beneath (video slowed down for dramatic impact):
When do you have to use Liquid Glass
The thought of Liquid Glass is that it acts as a layer on high of your app’s UI. In follow it will often imply that your essential app content material isn’t constructed utilizing the glass type. Doing so would lead to some fairly dangerous wanting UI as you’ll be able to see on this video:
On this video, I utilized a glass impact to all of my listing rows. The result’s a brilliant bizarre interface that overuses Liquid Glass.
As a substitute, Liquid Glass ought to be utilized to parts that sit on high of your UI. Examples embody toolbars, tab bars, floating motion buttons and related elements.
An instance of this may be seen proper right here in Maxine:
The default tab bar is a Liquid Glass part that overlays my listing. The floating plus button additionally has a glass impact utilized to it though you’ll be able to barely see it because of the mild background.
The purpose is that Liquid Glass parts ought to all the time be designed as sitting “on high” of one thing. They don’t stack, they’re not a part of your essential UI, they’re all the time on their very own layer while you’re designing.
Now, I’m not a designer. So should you can give you an effective way to make use of Liquid Glass that locations a component in your essential content material. I’m not going to let you know which you can’t or shouldn’t; you most likely know a lot better than I do. That mentioned, Apple’s philosophy for Liquid Glass is a layered design so for security you must most likely persist with that.
Making use of a Liquid Glass impact to UI parts
Let’s construct out a pleasant UI factor that may actually profit from a Liquid Glass feel and appear. It’s a UI factor that existed in an app referred to as Path which now not exists, and the UI factor hasn’t actually been used a lot since. That mentioned, I just like the interplay and I believe it’ll be enjoyable to present it a glass overhaul.
Our Start line
You possibly can see an instance of the button and its UI proper right here:
It takes fairly some code to attain this impact, and most of it isn’t related to Liquid Glass. That’s why you’ll be able to check out the ultimate code proper right here on GitHub. There’s a department for the start line in addition to the tip end result (essential) so you’ll be able to mess around a bit should you’d like.
The view itself appears like this:
struct ContentView: View {
@State personal var isExpanded = false
var physique: some View {
ZStack(alignment: .bottomTrailing) {
Coloration
.clear
.overlay(
Picture("bg_img")
.resizable()
.scaledToFill()
.edgesIgnoringSafeArea(.all)
)
button(kind: .residence)
button(kind: .write)
button(kind: .chat)
button(kind: .e-mail)
Button {
withAnimation {
isExpanded.toggle()
}
} label: {
Label("Residence", systemImage: "listing.bullet")
.labelStyle(.iconOnly)
.body(width: 50, top: 50)
.background(Circle().fill(.purple))
.foregroundColor(.white)
}.padding(32)
}
}
personal func button(kind: ButtonType) -> some View {
return Button {} label: {
Label(kind.label, systemImage: kind.systemImage)
.labelStyle(.iconOnly)
.body(width: 50, top:50)
.background(Circle().fill(.white))
}
.padding(32)
.offset(isExpanded ? kind.offset : .zero)
.animation(.spring(period: kind.period, bounce: 0.2))
}
}
This view by itself isn’t all that fascinating, it accommodates a few buttons, and making use of a liquid glass impact to our buttons shouldn’t be too arduous.
Making use of a glass impact
To make buttons appear like Liquid Glass, you apply the glassEffect
view modifier to them:
Button {
withAnimation {
isExpanded.toggle()
}
} label: {
Label("Residence", systemImage: "listing.bullet")
.labelStyle(.iconOnly)
.body(width: 50, top: 50)
.background(Circle().fill(.purple))
.foregroundColor(.white)
}
.glassEffect()
.padding(32)
After making use of the liquidGlass
modifier to all buttons the app appears like this while you run it:
We’re not seeing a glass impact in any respect!
That’s as a result of we additionally set a background on our buttons, so let’s go forward and take away the background to see what our view appears like:
Button {
withAnimation {
isExpanded.toggle()
}
} label: {
Label("Residence", systemImage: "listing.bullet")
.labelStyle(.iconOnly)
.body(width: 50, top: 50)
.foregroundColor(.white)
}
.glassEffect()
.padding(32)
If we run the app now, our UI appears like this:
Our icons are a bit arduous to learn and I’m truthfully not precisely certain whether or not this can be a beta bug or whether or not it’s purported to be this fashion.
Observe that Button
additionally comes with a .glass
button type that you should utilize. This impact is barely totally different from what I’ve used right here however I discover that the button type doesn’t all the time permit for the sorts of customizations that I like.
You possibly can apply the glass button type as follows:
Button {
withAnimation {
isExpanded.toggle()
}
} label: {
Label("Residence", systemImage: "listing.bullet")
.labelStyle(.iconOnly)
.body(width: 50, top: 50)
.foregroundColor(.white)
}
.buttonStyle(.glass)
.padding(32)
That mentioned, there are two issues I’d love to do at this level:
- Apply a background tint to the buttons
- Make the buttons seem interactive
Let’s begin with the background coloration.
Making use of a background coloration to our glass impact
To type our buttons with a background coloration, we have to tint our glass. Right here’s how we will do this:
Button {
withAnimation {
isExpanded.toggle()
}
} label: {
Label("Residence", systemImage: "listing.bullet")
.labelStyle(.iconOnly)
.body(width: 50, top: 50)
.foregroundColor(.white)
}
.glassEffect(.common.tint(.purple))
.padding(32)
This already appears lots higher:
Discover that the buttons nonetheless have a round form though we’re not explicitly drawing a circle background. That’s the default type for elements that you simply apply a glassEffect
to. You’ll all the time get a form that has rounded corners that match properly with the remainder of your app’s UI and the context the place the impact is utilized.
I do really feel like my buttons are a bit too opaque, so let’s apply a little bit of opacity to our tint coloration to get extra of a see-through impact:
Button {
withAnimation {
isExpanded.toggle()
}
} label: {
Label("Residence", systemImage: "listing.bullet")
.labelStyle(.iconOnly)
.body(width: 50, top: 50)
.foregroundColor(.white)
}
.glassEffect(.common.tint(.purple.opacity(0.8))
.padding(32)
That is what our view appears like now:
After I faucet the buttons now, not lots occurs as proven within the video above. We are able to do higher by making our buttons reply to person interplay.
Making an interactive glass impact
To make our glass buttons reply to person enter by rising a bit and making use of a kind of shimmer impact, we apply the interactive
modifier to the glass impact:
Button {
withAnimation {
isExpanded.toggle()
}
} label: {
Label("Residence", systemImage: "listing.bullet")
.labelStyle(.iconOnly)
.body(width: 50, top: 50)
.foregroundColor(.white)
}
.glassEffect(.common.tint(.purple.opacity(0.8).interactive())
.padding(32)
That is what our interactions appear like now:
Our UI is coming collectively. With the glassEffect
view modifier, the interactive
modifier and a tint
we managed to construct a fairly compelling impact.
Nevertheless, our UI isn’t fairly liquid. You’re distinct buttons performing an impact.
We are able to group our parts collectively to make it seem as if they’re all coming from the identical drop of glass.
This sounds a bit bizarre so let’s simply leap into an instance instantly.
Grouping Liquid Glass parts collectively
The very first thing we should always do now that we have now a gaggle of parts which might be all utilizing a Liquid Glass impact is group them collectively in a container. This can be a suggestion from Apple that helps make certain the system can render our results effectively:
GlassEffectContainer {
button(kind: .residence)
button(kind: .write)
button(kind: .chat)
button(kind: .e-mail)
Button {
withAnimation {
isExpanded.toggle()
}
} label: {
Label("Residence", systemImage: "listing.bullet")
.labelStyle(.iconOnly)
.body(width: 50, top: 50)
.foregroundColor(.white)
}
.glassEffect(.common.tint(.purple.opacity(0.8)).interactive())
.padding(32)
}
We are able to make a number of Liquid Glass elements act as a single “unit” by grouping them collectively. To do that, we have to outline a namespace that our UI parts can share:
struct ContentView: View {
@Namespace var glassNamespace
var physique: some View { /* ... */ }
}
Subsequent, we will group our parts collectively within that namespace utilizing the glassEffectID
view modifier. This modifier requires a namespace and a novel identifier for every button in our part. The purple button would look as follows:
Button {
withAnimation {
isExpanded.toggle()
}
} label: {
Label("Residence", systemImage: "listing.bullet")
.labelStyle(.iconOnly)
.body(width: 50, top: 50)
.foregroundColor(.white)
}
.glassEffect(.common.tint(.purple.opacity(0.8)).interactive())
.glassEffectID("menu", in: glassNamespace)
We add glassEffectID
to each factor that has a glass impact utilized.
By doing this, the weather will mix collectively after they’re shut to one another within the UI. For instance, once we place all buttons in an HStack
with no spacing, they find yourself wanting like this:
As a result of all the weather are in the identical GlassEffectContainer
and so they share a glassEffectID
namespace, we will now run our animation and have the buttons animate in a fluid method:
I’ve slowed every part down a bit so you’ll be able to benefit from the impact and see that the elements all originate from a single button, making them appear like a liquid.
The mathematics to attain all that is a part of the ButtonType
enum within the GitHub repository which you can take a look at if you wish to see precisely how the tip end result was achieved.
In Abstract
Liquid glass won’t be your factor and that’s completely high-quality. That mentioned, it permits us to experiment with UI in enjoyable ways in which would possibly shock you.
On this put up, you realized in regards to the glassEffect
modifier in addition to the glassEffectID
view modifier to construct a enjoyable menu part that may present and conceal itself utilizing a enjoyable, fluid animation.
If you wish to see the tip end result or use this code, be at liberty to drag it from GitHub and modify it to fit your wants.