How to create Neumorphic design in SwiftUI

⋅ 8 min read ⋅ SwiftUI Tutorial Neumorphism

Table of Contents

Recently, there is a new design trend in a design community called Neumorphism.

If you remembered pre-iOS 7 UI, you probably know the term Skeuomorphism.

Skeuomorphism is a term most often used in graphical user interface design to describe interface objects that mimic their real-world counterparts in how they appear and/or how the user can interact with them.

Apple brings this style in the early day of iOS to make everyone can quickly adopt their brand new touch screen phone. By making every UI element look like real-world counterparts, it gives everyone a clue on how to interact with the new system. And it looks like it works, everyone can pick up the iOS interface in no time.

iOS Calculator App
iOS Calculator App
The calculator app's buttons look like a real-world button, and the layout also resembles the real calculator.
iOS Newsstand
iOS Newsstand
The newsstand app also mimics wooden magazine stand.


All the system icons also design in a way that mimics real-world objects.

Comparison of iOS Skeuomorphism and Flat Design
Comparison of iOS Skeuomorphism (Before iOS 7) and Flat Design (iOS 7 onward)

Apple decided to drop this design out in iOS 7. Since everyone knows how to interact with the system now, there is no point to mimic the real-world object[1] and came to the era of flat design.

Neumorphism is a modern take on Skeuomorphism in 2020, it mimics real-world objects, but design it in a minimal and subtle way.

The following are the Dribble sample shots of this style https://dribbble.com/search/skeuomorph and a popular shot https://dribbble.com/shots/8297803-Skeuomorph-Mobile-Banking-Continuation

Skeumorphism trend in Dribbble
Skeumorphism trend in Dribbble 2020

Enough for a brief introduction. Love it or hate it, I will show you how to this in SwiftUI (I personally hate it).

Two Drop Shadow

There are many variations of Neumorphism, but all of them are a combination of different shadow effects. The one I am going to teach you is the simplest form.

The following example is a Dribble shot I found; we are going to implement the one on the left.

Neumorphism examples
Neumorphism shot by sonal shah

Just like old-skeuomorphism time, we can accomplish this neumorphic design with two drop shadows, one for light and one for shadow, each at opposite ends of an element.

Let's see how to build a button with this design trend.

.shadow()

SwiftUI has a view modifier to apply shadow to a view, and luckily it can easily stack. So we can have two shadows, one for light effect and one for shadow with two lines of code.

.shadow(color: .dropShadow, radius: 15, x: 10, y: 10)
.shadow(color: .dropLight, radius: 15, x: -10, y: -10)

Neumorphism light source is coming from the top left of the screen (I don't know why, but everyone seems to make it this way). So our shadow offset is (10, 10) and light offset is (-10, -10).

That's probably all you need to do for this simple variation.

struct NeumorphicView: View {
var body: some View {
VStack {
Button("Hello, Neumorphism!", action: {

}).padding(20)
.background(
RoundedRectangle(cornerRadius: 10)
.fill(Color.neuBackground)
)
.shadow(color: .dropShadow, radius: 15, x: 10, y: 10)
.shadow(color: .dropLight, radius: 15, x: -10, y: -10)
.foregroundColor(.primary)
}
}
}

The above code would result in something like this:

Neumorphism with two drop shadow
Neumorphism with two drop shadow

You can easily support sarunw.com by checking out this sponsor.

Sponsor sarunw.com and reach thousands of iOS developers.

Color sensitive

Neumorphism is quite sensitive to color; it won't look good on black and white background. Make sure you set background to non-white for maximum light and shadow effect.

Neumorphism on white background
Neumorphism on white background

In the above example, I set background color the same color as the button.

struct ContentView: View {
var body: some View {
NeumorphicView()
.frame(maxWidth: .infinity,
maxHeight: .infinity)
.background(Color.neuBackground)
.edgesIgnoringSafeArea(.all)
}
}

The thing gets worse on a colorful background.

White and black shadow on colorful background
White and black shadow on colorful background

To make a generic neumorphic view, instead of hard code shadow and lighting color like my above example, we need to make a shadow effect apply beautifully regardless of a button or background color. We can do that with .blendMode.

Blend mode

Blend mode is a way to compositing the view with overlapping views. In our case, we want to blend shadow and lighting to underlying views.

First, we change the color of shadow to .white and .black. I find it easier to use with blend mode. Next, we apply blendMode to those shadows. What blend mode to use is trial and error for me, I just rotate through the options and see what fit, in this case, .overlay do the trick.

struct NeumorphicView: View {
var bgColor: Color

var body: some View {
VStack {
Button("Hello, Neumorphism!", action: {

}).padding(20)
.background(
ZStack {
RoundedRectangle(cornerRadius: 10, style: .continuous)
.shadow(color: .white, radius: 15, x: -10, y: -10)
.shadow(color: .black, radius: 15, x: 10, y: 10)
.blendMode(.overlay)
RoundedRectangle(cornerRadius: 10, style: .continuous)
.fill(bgColor)
}
)
.foregroundColor(.primary)
}
}
}

We move .shadow into .background because apply .blendMode will apply to all child views. We want to keep our button color intact and not effect by blend mode. We also create another RoundedRectangle to use as a button background color.

This new view work pretty well on color background.

Neumorphic view with blend mode shadow
Neumorphic view with blend mode shadow

Animation

Now, we have a beautiful neumorphic button, but it still a static button. Taping on it would do nothing. That's not a spirit of skeuomorphism. Let's make it move when we tap it.

Pressed state

Before we implement it, let's decided on our pressed state.

We could use the one on the right of the following example, but I don't think that's how this type of button looks when it is pressed.

Neumorphism examples
Neumorphism shot by sonal shah

Let's look at the real-world examples of these buttons. I'm not an expert on button or design, but what I think is the most resemble to this neumorphic design is Japanese toilet buttons. If you have been to Japan, you know what I'm talking about. If you haven't, here are some examples.

Japanese toilet control panel
Japanese toilet control panel by William Kumberger on Flickr
Japanese toilet control panel
Japanese toilet control panel by Pawel Loj on Flickr

When you push these buttons, it doesn't go all the way down. It just moves down a bit. To achieve this, we just shrink down the button and reduce the shadow radius to mimic that the button is pushed down.

Pressed state
Normal state (Left) and Pressed state (Right)

Implementation

We got everything in place, let's do the coding part.

First, we add a new @State to keep track of the button state.

@State private var isPressed: Bool = false

Then, use isPressed to control the value of shadow radius and position.

.shadow(color: .white, radius: self.isPressed ? 7: 10, x: self.isPressed ? -5: -10, y: self.isPressed ? -5: -10)
.shadow(color: .black, radius: self.isPressed ? 7: 10, x: self.isPressed ? 5: 10, y: self.isPressed ? 5: 10)

Scale button a bit to mimic that it is pushing down.

.scaleEffect(self.isPressed ? 0.98: 1)

Lastly, make all of this animate with spring animation.

.animation(.spring())

The following is the final result:

struct NeumorphicView: View {
var bgColor: Color
@State private var isPressed: Bool = false

var body: some View {
VStack {
Button("Hello, Neumorphism!", action: {
self.isPressed.toggle()
}).padding(20)
.background(
ZStack {
RoundedRectangle(cornerRadius: 10, style: .continuous)
.shadow(color: .white, radius: self.isPressed ? 7: 10, x: self.isPressed ? -5: -10, y: self.isPressed ? -5: -10)
.shadow(color: .black, radius: self.isPressed ? 7: 10, x: self.isPressed ? 5: 10, y: self.isPressed ? 5: 10)
.blendMode(.overlay)
RoundedRectangle(cornerRadius: 10, style: .continuous)
.fill(bgColor)
}
)
.scaleEffect(self.isPressed ? 0.98: 1)
.foregroundColor(.primary)
.animation(.spring())
}
}
}

Here is the animation showing how the final result looks like.

Pressed state
Pressed state

Conclusion

I can see why this neumorphism got all the hype recently, people getting bored with flat and come back to good old skeuomorphism with a new variation. Personally, I don't like it. I can't see myself using all the apps that look like this, but with the right amount of it in some specific places might put joy in your app. When the time comes, make sure you know how it is done.

In this article, I focus on the aesthetic aspect of the button. In the next article, I will write about the right way to implement this kind of styling with ButtonStyle. Subscribe to my newsletter or follow me on Twitter so you don't miss it.

You can easily support sarunw.com by checking out this sponsor.

Sponsor sarunw.com and reach thousands of iOS developers.
  1. Actually, skeuomorphism still used in the Apple interface system, but it becomes more and more seamless. One example is all the gestures Apple keeps adding all these years. Those gestures mimic our way of interacting with a real-world object. It backed by the concept of weight and force of the real object. There is a really nice WWDC 2018 session about this Designing Fluid Interfaces. ↩︎


Read more article about SwiftUI, Tutorial, Neumorphism, or see all available topic

Enjoy the read?

If you enjoy this article, you can subscribe to the weekly newsletter.
Every Friday, you'll get a quick recap of all articles and tips posted on this site. No strings attached. Unsubscribe anytime.

Feel free to follow me on Twitter and ask your questions related to this post. Thanks for reading and see you next time.

If you enjoy my writing, please check out my Patreon https://www.patreon.com/sarunw and become my supporter. Sharing the article is also greatly appreciated.

Become a patron Buy me a coffee Tweet Share
Previous
How to remove Cocoapods from your project

Swift Package Manager is getting better every day. It is a matter of time before everyone supports it. When the time comes, make sure you know how to say goodbye to this old friend.

Next
Class-only Protocols: class or AnyObject

If you are still declaring class-only protocols by inheriting them from class, you might need to revise your knowledge.

← Home