LastPass clone - part 9: Password Generation UI

SwiftUI May 10, 2020

Hey guys, last week we made the EditFormView, and in this one and the next, we will create the password generation functionality. We will start by creating the user interface as it will be a little bit involved. Let’s get started.

Preparations

You should have the source code link in your email inbox if you are subscribed, otherwise click here to subscribe and get the source code.

Password generator view

Start by creating a swift UI file in the Views folder named PasswordGeneratorView.swift. Now add the following properties to the top of the struct:

    @State private var password = ""
     @Binding var generatedPassword: String
     @State private var lowercase = true
     @State private var uppercase = false
     @State private var specialCharacters = true
     @State private var digits = false
     @State private var length: CGFloat = 6

The above properties will be changed when we implement the password generation service which will implement the ObservableObject protocol.

Then replace everything in body with the following:

VStack(spacing: 30) {
            Text("Generate password").font(.title).bold()
            
            Group {
                SharedTextfield(value: self.$password, header: "Generated Password", placeholder: "Your new password", errorMessage: "", showUnderline: false, onEditingChanged: { flag in
                    }).padding()
            }.background(Color.background)
                .cornerRadius(10).neumorphic()
                
            VStack(spacing: 10){
                Toggle(isOn: $lowercase) {
                    Text("Lowercase")
                }
                
                Toggle(isOn: $uppercase) {
                    Text("Uppercase")
                }
                
                Toggle(isOn: $specialCharacters) {
                    Text("Special Characters")
                }
                
                Toggle(isOn: $digits) {
                    Text("Digits")
                }
                
                    
                Slider(value: $length, in: 1...30, step: 1)
                    .accentColor(Color.accent)
                
            }.padding()
                .background(Color.background)
				.cornerRadius(10)
                .neumorphic()

        }.padding(.horizontal)
        	.frame(maxHeight: .infinity)
         	.background(Color.background)
        	.edgesIgnoringSafeArea(.all)

The above code might seem enormous, but it’s way simpler than you think. Here is what it does:

  1. We create a text that will act as a header title for this view.
  2. We then use our SharedTextfield that we created in one of previous parts to create a field that will display the generated password. Here we use a textfield rather than a simple text label because we want to give the user an option to change if he wishes. We then add some corner radius and made it neumorphic using our own neumorphic modifier.
  3. Next, we put four Toggles and a slider inside a vertical Stack container. Right now, those toggles have the built-in boring styles, but we will create an amazing and beautiful neumorphic toggle design later on. Next, we add a bunch of modifiers to style the second VStack.
  4. Last, we also add modifiers to the outer VStack to make it look beautiful. Remember the order of modifiers matters.

Currently the preview is broken because of the error that we have, so replace the PasswordGeneratorView() in the preview with the following:

  PasswordGenerationView(generatedPassword: .constant(""))

Resume the preview, and you should see something like this:

As you can see the screen looks good, but the toggles are boring, let’s create a custom toggle neumophic style next.

Custom toggle style

Let’s start by creating a new folder that we will name Styles, inside it add a swift file named CustomToggleStyle.swift containing the following code:

import SwiftUI

struct CustomToggleStyle: ToggleStyle {
    
    
    func makeBody(configuration: Self.Configuration) -> some View {
     
        EmptyView()
    }
}

In order for us to use the custom toggle style, it needs to conform to the ToggleStyle protocol which in turn gives us the makeBody function which is required to customise the toggle that uses this style. Let’s start with something simpler.

Replace the EmptyView() with the following code:

Button(action: {
            configuration.isOn.toggle()
        }) {
            if configuration.isOn {
                Circle().frame(width: 50, height: 50).foregroundColor(Color.green)
            } else {
                Circle().frame(width: 50, height: 50).foregroundColor(Color.red)
            }
        }
        

Here is what we are doing here:

  1. The configuration has a isOn which indicates whether the toggle is on or off. This field is a boolean binding property which means that we can also mutate it using the toggle() function. We toggle the isOn property in the button’s action to switch states.
  2. Next, we return a simple green Circle() when isOn is set to true or a red Cirlce() otherwise. This the logic we will eventually use to toggle on and off the neumorphic design.

To test this, open the password generation view, and add the following modifier to each toggle:

.toggleStyle(CustomToggleStyle())

After making that change, you will need to resume the preview and click the play button to run the it, and try clicking the toggles… You will notice that there are switching from red to green and vice-versa.

Here what we got now:

Before we start making the neumorphism design, let’s first add the label that was there before. Now replace the entire block of code in the makeBody function with the following:

        HStack {
            configuration.label
            Spacer()
            Button(action: {
                configuration.isOn.toggle()
            }) {
                if configuration.isOn {
                    Circle().frame(width: 50, height: 50).foregroundColor(Color.green)
                } else {
                    Circle().frame(width: 50, height: 50).foregroundColor(Color.red)
                }
            }
        }

What we’ve done here is we put the label and the existing button inside and HStack container. You might be wondering where’s the label coming from, well it’s coming from the configuration. Go back to the PasswordGeneratorView , and resume the preview again.

Check out the preview now:

As you can see in the above preview, the label is the Text you passed in the Toggle. Now let’s make that neumorphic design now, shall we?

We will start with the simple state which is when the toggle is off. So go back in the CustomToggleStyle, and replace the else content which is the red circle with the following:

Circle() .frame(width: size, height: size)
          .foregroundColor(Color.background)

           Image(systemName: "power")
                 .resizable()
                 .frame(width: 20, height: 20)
                 .foregroundColor(Color.gray)
}.cornerRadius(size / 2).neumorphic()

We are missing the size property hence the errors, so add the following to the top:

 var onColor: Color = .background
 var offColor: Color = .darkerAccent
 var size: CGFloat = 40

Now go back to PasswordGeneratorView, resume the preview, and you should see this:

As you can see in the above picture, the off state will be convex which means we want to make the on state concave. We can create concave neumophic designs using inner shadow, but there’s a problem, swift UI does not have inner shadows, so we will try to create our own.

There are a bunch of ways one can create this concave neumophic design, but I prefer the one made by Paul Hudson from HackingWithSwift. So let’s start making it.

In the Extensions folder, create a swift file named LinearGradientExt containing the following block of code:

extension LinearGradient {
    init(_ colors: Color...) {
        self.init(gradient: Gradient(colors: colors), startPoint: .topLeading, endPoint: .bottomTrailing)
    }
}

Here we are just extending the LinearGradient with yet another initialiser, this one will just take in a gradient object, the start and end point will be the same for what we are trying to do, that’s why there’s no need to pass them in as parameters.

Next, let’s extend the View protocol as well. In the same Extensions folder, add another swift file named ViewExt containing the following code:

 func innerShadow(radius: CGFloat, colors: (dark: Color, light:Color)) -> some View {
        self.overlay(
            Circle()
                .stroke(colors.dark, lineWidth: 4)
                .blur(radius: radius)
                .offset(x: radius, y: radius)
                .mask(Circle().fill(LinearGradient(colors.dark, Color.clear)))
        )
    }

Here is what that code he is doing:

  1. We add an overlay to the view that will use this modifier.
  2. Inside the overlay, we add a circle
  3. Then stroke it with the dark color used for the darker side of our neumorphic design.
  4. Next, we add a blur modifier with the same radius as the offset value we will add next.
  5. Next, we move the blurred view a little to the bottom right.
  6. We then mask the view with yet another circle filled with a linear gradient.

That was one side which is the top left side, we are missing the bottom right.

Add another overlay directly after first one with the following code:

.overlay(
                Circle()
                    .stroke(colors.light, lineWidth: 8)
                    .blur(radius: radius)
                    .offset(x: -radius, y: -radius)
                    .mask(Circle().fill(LinearGradient(Color.clear, colors.dark)))
        )

The above code will create the same overlay as the one above but in the opposite side. This one goes from bottom right to top left whereas the first one went from top left to bottom right.

Next, in the CustomToggleStyle, replace the following code:

Circle().frame(width: 50, height: 50).foregroundColor(Color.green)

With the following:

ZStack {
                        Circle()
                        .fill(Color.background)
                            .frame(width: size, height: size)
                            
                        
                        Image(systemName: "power")
                        .resizable()
                        .frame(width: 20, height: 20)
                            .foregroundColor(Color.accent)
                    }

Now, open the PasswordGeneratorView and resume the preview, then run it. You should see something like this:

Neumorphic button in swift ui
Neumorphic button in swift ui

As you can see, there’s still no concave neumorphic design on the toggle when it’s selected. Well that’s because we still haven’t added the the design we’ve created. So, add the following after the .frame(width: size, height: size) modifier in the CustomToggleStyle:

.innerShadow(radius: 1, colors: (Color.darkShadow, Color.lightShadow))

Now if you resume and run the preview, you should be able to click the toggle and see your concave neumorphic design.

And with this, we are done with this part . In the next one, we will finish the password generation process by creating its brain. Please feel free to share this article, subscribe if you haven’t done so already. Stay tuned for more and happy coding.    

Support my work by becoming a patreon

John K

I am a software developer and code enthusiast. Do you want to work with me, have a suggestion or a request? Feel free to contact me at [email protected] or https://twitter.com/liquidcoder