As a Swift developer I am always seeking for new fresh looking designs which are timeless, simple and minimalistic. I was searching for a view in the style of a muted or frosted glass view, which is heavily used across iOS and macOS and even more in a more 3D-looking way in the new visionOS. It seem like Apple is using this style everywhere like for the control center (macOS) and the App library (iOS/iPadOS):
So I was searching along the web but I didn’t quite find what I wanted unless I stumbled across this video from Kavsoft. Then I used this style but adapted it a little more to make it look even more cleaner and less shiny.
Now what I am using is very simple yet elegant:
struct WelcomeView: View {
var body: some View {
ZStack {
Color.purple
.ignoresSafeArea()
RoundedRectangle(cornerRadius: 20.0)
.fill(.white)
.opacity(0.25)
.shadow(radius: 10.0)
.padding()
}
}
}
And voilá, this is what we get:
It should be mentioned that a vibrant background color or even gradient let’s shine the new GlassView more better that just a plain color or even white background. So here we go (credit agin to Kavsoft for the inspo):
But let’s just rewind here. So all we need is a vibrant background. It should have at least one ore two colors. This is also true for my examples from Apple I illustrated at the top. Imagine the app library on iOS when the background is just plain white.
Things for a great frosted or muted GlassView:
- Colorful background
- At least a more grey-ish background, since plain black obsorbs all the shadows
- More GlassViews on the screen will look better than just a signle one
And that’s basically all you need, but now we can of course factor a lot of things out and make it way more customizable, so let’s…
Think bigger
Now we can extract the corner radius, the fill color, opacity and shadow radius:
struct GlassView: View {
let cornerRadius: CGFloat
let fill: Color
let opacity: CGFloat
let shadowRadius: CGFloat
init(cornerRadius: CGFloat, fill: Color = .white, opacity: CGFloat = 0.25, shadowRadius: CGFloat = 10.0) {
self.cornerRadius = cornerRadius
self.fill = fill
self.opacity = opacity
self.shadowRadius = shadowRadius
}
var body: some View {
RoundedRectangle(cornerRadius: cornerRadius)
.fill(fill)
.opacity(opacity)
.shadow(radius: shadowRadius)
}
}
Additionally, let’s make some view modifiers to make it super easy to have some glass views everywhere.
struct GlassModifier: ViewModifier {
let cornerRadius: CGFloat
let fill: Color
let opacity: CGFloat
let shadowRadius: CGFloat
func body(content: Content) -> some View {
content
.background {
GlassView(cornerRadius: cornerRadius, fill: fill, opacity: opacity, shadowRadius: shadowRadius)
}
}
}
extension View {
func glass(cornerRadius: CGFloat, fill: Color = .white, opacity: CGFloat = 0.25, shadowRadius: CGFloat = 10.0) -> some View {
modifier(GlassModifier(cornerRadius: cornerRadius, fill: fill, opacity: opacity, shadowRadius: shadowRadius))
}
}
And now we can have in just one line of code a GlassView of basically every view you want (but of course we need some additional padding):
struct ContentView: View {
var body: some View {
ZStack {
// Vibrant Background
...
// Glass View
Text("Hello World! 🌐")
.padding() // Additional padding
.glass(cornerRadius: 20.0)
}
}
}
And now let’s lift all of this to the super-next level:
We can also have a GlassList, a GlassPane, and a GlassLazyVGrid:
struct GlassList<T: Identifiable, Content: View>: View {
var items: [T]
let cornerRadius: CGFloat
let delete: (IndexSet) -> Void
let lastCellSpacing: CGFloat
var rowContent: (T) -> Content
init(items: [T], with cornerRadius: CGFloat, onDelete: @escaping (IndexSet) -> Void = {_ in }, lastCellSpacing: CGFloat = 25, @ViewBuilder rowContent: @escaping (T) -> Content) {
self.items = items
self.cornerRadius = cornerRadius
self.delete = onDelete
self.lastCellSpacing = lastCellSpacing
self.rowContent = rowContent
}
var body: some View {
List {
ForEach(items) { item in
self.rowContent(item)
.glassCellView(cornerRadius: cornerRadius, corners: nil)
}
.onDelete(perform: delete)
}
.listStyle(.plain)
}
}
struct GlassPane<Content: View>: View {
let cornerRadius: CGFloat
let corners: UIRectCorner?
let fill: Color
let opacity: CGFloat
let shadowRadius: CGFloat
let content: Content
init(with cornerRadius: CGFloat, corners: UIRectCorner? = .allCorners, fill: Color = .white, opacity: CGFloat = 0.25, shadowRadius: CGFloat = 10.0, @ViewBuilder content: @escaping () -> Content) {
self.cornerRadius = cornerRadius
self.corners = corners
self.fill = fill
self.opacity = opacity
self.shadowRadius = shadowRadius
self.content = content()
}
var body: some View {
ZStack {
GlassView(cornerRadius: cornerRadius, corners: corners, fill: fill, opacity: opacity, shadowRadius: shadowRadius)
content
}
}
}
struct GlassLazyVGrid<T: Identifiable, Content: View>: View {
let items: [T]
let cornerRadius: CGFloat
let columns: [GridItem]
let spacing: CGFloat?
let content: (T) -> Content
init(items: [T], cornerRadius: CGFloat, columns: [GridItem], spacing: CGFloat, @ViewBuilder content: @escaping (T) -> Content) {
self.items = items
self.cornerRadius = cornerRadius
self.columns = columns
self.spacing = spacing
self.content = content
}
var body: some View {
LazyVGrid(columns: columns, spacing: spacing) {
ForEach(items) { item in
GlassPane(with: cornerRadius) {
content(item)
}
}
}
}
}
So I hope this might help others to get quick access to a very cool and modern looking design for Views, Lists or even Grids.
SwiftPackage
Here you can find all of this and more inside my Swift Package SwiftUIGlass so you all can benefit and improve the GlassView.
PS: Back in the days of Minecraft my favorite material was, of course glass.
Thanks for reading and feel free to give me some feedback on my first medium article. You can always connect with me via LinkedIn or directly here.
Have a nice day ☀️🌾