Neumorphism UI in SwiftUI
Maybe you have seen UI designs using the neumorphism style or maybe you have never heard of it. Neumorphism is a design style to make elements appear to protrude or dent into the screen. Pages like neumorphism.io let you quickly create these elements for websites or anywhere you use HTML.
Prerequisites
If you would like to follow along, you will need:
- Mac with XCode installed
- New or existing XCode Project supporting iOS 13.0+ or macOS 10.15+
Neumorphism UI Elements
There are two main UI elements in Neumorphism: One that is protruding and one that is indented. In either case we first have to do some preparation.
Creating Color Assets
Before we can get started we need to add all the colors we need to our App. Click on Assets in the project navigator, and then click on the + icon at the bottom of the Asset list, and select Color Set.
Name the color MainColor and add the hexadecimal value #E0E0E0 in the attribute inspector as shown below:
You can use for the dark version either the same color, or define a separate color for the dark theme of your app.
Repeat the step of creating a new Color Set two more times, so you have three Color Sets in the end with the follow Hex values:
- HighlightColor: #FFFFFF
- ShadowColor: #BEBEBE
With the colors all set, we can create our UI elements.
Creating a Protruding Element
First we want to create a element that looks like it is protruding out of the screen. First we need the general shape of the element and then we need to shadows in the opposite directions.
Basic Shape
Open you ContentView.swift and replace everything in the body of the content view with a HStack {} and inside the HStack create a RoundedRectangle with a corner radius of 20 and width & height of 200. In the end we use our MainColor and apply it as a background to the HStack.
struct ContentView: View {
var body: some View {
HStack {
// RoundedRectangle as the base shape
RoundedRectangle(cornerSize: CGSize(width: 20, height: 20))
.frame(width: 200, height: 200)
.padding()
}
.background(Color(.main))
}
}
#Preview {
ContentView()
}
Note: You can reference a Color Set from your Assets directly in Code. For example, if your Color Set is called MainColor you can reference it in Code with Color(.main).
Shadows
The shape needs to have the same color as the background. Right after creating the RoundedRectangle we simple can apply a .fill(Color(.main)) modifier to give the correct color.
As expected after adding the fill, we are not able to see our rectangle anymore, as it has the same color as the background. So let’s add the shadow directly too. Add the .shadow() modifier to the fill color with the following settings:
- .drop: highlightColor, radius: 20, x: -10, y: -10
- .drop: shadowColor, radius: 20, x: 10, y: 10
Notice how the x and y coordinates are the opposites for the two different colors. Your ContentView should look now like this:
struct ContentView: View {
var body: some View {
HStack {
RoundedRectangle(cornerSize: CGSize(width: 20, height: 20))
.fill(
Color(.main)
.shadow(
.drop(color: Color(.highlight), radius: 20, x: -10, y: -10)
)
.shadow(
.drop(color: Color(.shadow), radius: 20, x: 10, y: 10)
)
)
.frame(width: 200, height: 200)
.padding()
}
.background(Color(.main))
}
}
If you have a preview enabled you should be able to see the result directly and it should look similar to the below (depending if you are building an iOS app or MacOS app).
Creating a Indented Element
Indented elements are created very similar, the only difference is that instead of using a drop shadow we will be using a inner shadow. The rest of the code is staying the same.
Copy the code for our protruding rectangle and past it right below, still inside the HStack{}. Replace the two .drop modifiers with .inner and we are done!
Below is the final code of the ContentView.
struct ContentView: View {
var body: some View {
HStack {
// Protruding Element
RoundedRectangle(cornerSize: CGSize(width: 20, height: 20))
.fill(
Color(.main)
.shadow(
.drop(color: Color(.highlight), radius: 20, x: -10, y: -10)
)
.shadow(
.drop(color: Color(.shadow), radius: 20, x: 10, y: 10)
)
)
.frame(width: 200, height: 200)
.padding()
// Indented Element
RoundedRectangle(cornerSize: CGSize(width: 20, height: 20))
.fill(
Color(.main)
.shadow(
.inner(color: Color(.highlight), radius: 20, x: -10, y: -10)
)
.shadow(
.inner(color: Color(.shadow), radius: 20, x: 10, y: 10)
)
)
.frame(width: 200, height: 200)
.padding()
}
.frame(width: 600, height: 400)
.background(Color(.main))
}
}
#Preview {
ContentView()
}
If you want to change the size of the rectangle shapes, you may also want to adjust the shadow radius and x & y offsets.
Thank You! ❤️
Thank you for reading and I hope this was helpful! 👍 And if you enjoyed this post and want to help me write more stories, support me with caffeine on Ko-fi 😊
If you would like to see this being used in an actual app, I have created a small puzzle app using Neumorphism called Numbers, which you can download for free here.