How to create Neumorphic design in SwiftUI

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 Newsstand

The calculator app's buttons look like a real-world button, and the layout also resembles the real calculator.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 (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 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 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 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.

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).

I define a color and helper for this case (getting the code from Stackoverflow and color from this Dribble shot). extension Color {

static let neuBackground = Color ( hex : "f0f0f3" )

static let dropShadow = Color ( hex : "aeaec0" ) . opacity ( 0.4 )

static let dropLight = Color ( hex : "ffffff" )

}



extension Color {

init ( hex : String ) {

let scanner = Scanner ( string : hex )

scanner . scanLocation = 0

var rgbValue : UInt64 = 0

scanner . scanHexInt64 ( & rgbValue )



let r = ( rgbValue & 0xff0000 ) > > 16

let g = ( rgbValue & 0xff00 ) > > 8

let b = rgbValue & 0xff



self . init ( red : Double ( r ) / 0xff , green : Double ( g ) / 0xff , blue : Double ( b ) / 0xff )

}

}

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

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

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

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

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 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 by William Kumberger on Flickr

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.

Normal state (Left) and Pressed state (Right)

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

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.

Related Resources #

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

Tweet

Share

← Home