LastPass clone - part 7

SwiftUI Apr 27, 2020

Hey ladies and gents, in the previous article (part 6) we made the header view which contained the filter view, but we haven’t used it yet. In this article we will create the home view containing the HeaderView and a list of password and notes using the neumorphism design.

Preparations

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

HomeView

In the Screens, add a file named HomeView.swift. Before we create the list, we first need to create RowItems for it, we will do that by creating a new swift UI file in the Views folder, and name it RowItem .

Now, replace the Text() inside body with the following:

 HStack{
            Image("note")
                .resizable()
                .aspectRatio(contentMode: .fit)
                .frame(width: 50, height: 50)
            
            VStack(alignment: .leading) {
                Text("No name")
                    .font(.system(size: 20))
                    .padding(.bottom, 5)
                Text( "N/A")
                    .foregroundColor(Color.gray)
                    .font(.callout)
            }

Here is what I’ve done:

  1. We create an image view giving it a frame of 50 by 50. We need to make the image resizable in order for the frame to work, try commenting out the resizable modifier and you will see what I am talking about. We also set its aspect ratio to fit to prevent it from being cut-out.
  2. We then create a VStack containing two Text views. When you create a Text, the default alignment is center, but we want the alignment to be left or leading that’s why we gave the leading alignment to the parent VStack container.

Here is a preview of what we’ve just done above:

Values you see above are not final, we will change those when we introduce CoreData, but for now those will do the job.

Next up, let’s give that component some neumorphic design. First, In the NeumorphicEffect.swift file, add the following below the NeumorphicEffect struct:


extension View {
    func neumorphic() -> some View {
        self.modifier(NeumorphicEffect())
    }
}

Next, add the following modifiers to the HStack:

.padding(.horizontal)
        .frame(maxWidth: .infinity, minHeight: 80, alignment: .leading)
            .background(Color.background)
             .cornerRadius(20)
             .neumorphic()
             .padding(.vertical)

Here is what those are doing:

  1. We add some padding to the left and right
  2. We make the HStack fill the entire width with
  3. See how we call the neumorphic() modifier? That’s because we have extended the view Protocol, and made our own modifier.

Now, let’s go back to the HomeView.swift file, and create the list view.

Before we create the list, we first need to do some housekeeping.

In the Views folder, add a swiftUI file named SectionTitle.swift , and replace the Text with the following:

 Text(title)
            .font(.title)
            .frame(maxHeight: .infinity)
            .frame(width: UIScreen.main.bounds.width, alignment: .leading)
            .padding(.leading, 40)
            .background(Color.background)

And add the following property to the top:

var title: String

Here is what new are doing here:

  1. We want the title to fill the height of the parent hence we set the maxHeight to infinity which will make the height to match its parent’s height.
  2. As of the time of writing this article, there’s no way to customise the section header or footer’s colors, so in order to do so, we will need to cover the entire section header or footer with its child that’s why we set the width to be the device’s screen width.

Open the HomeView.swift file and put the following functions below body:

    private func createList() -> some View {
        
    }
    
    private func createPasswordsSection() -> some View {
        
    }
    
    private func createNotesSection() -> some View {
        
    } 

Don’t worry about the errors you are getting now, we will implement those functions one by one shortly. In the createList function, add the following:

 List{
            createPasswordsSection()
            createNotesSection()
        }

That’s the list for now, we will comeback later to give it some styles.

Now add the following in the createPasswordsSection function:

Section(header:
                SectionTitle(title: "Passwords")
            ) {
                ForEach(1..<5) { i in
                    RowItem()
                }
            }

And the following in the NotesSection function:

Section(header:
                SectionTitle(title: "Notes")
            ) {
                ForEach(1..<5) { i in
                    RowItem()
                }
            }

As you can see, we are re-using the SectionTitle for the section header and the RowItem for the list item. Later on, we will replace the hard-coded data with CoreData content, but for now that will do the trick.

Now let’s style the List. Add the following modifier directly to the it:

.onAppear {
            UITableView.appearance().backgroundColor = UIColor(named: "bg")
            UITableView.appearance().separatorColor = .clear
            UITableView.appearance().showsVerticalScrollIndicator = false
        }
As of time of writing this tutorial, SwiftUI List has limited customisation, there’s no way of:
1.  changing the List’s background color, 2.  removing the ScrollIndicator, 3. hiding separators and so on.

However, having been built on top of UIKit’s UITableView, we can use the latter to hack it, and that’s exactly what we are doing in the onAppear block.

UITableView has a static function that return the current instance of itself (UITableView), so we can get that instance, and customize it. Because the instance is shared, the List will pickup the change and apply them to the List.

Here is the preview of what we have done so far:

Yeah looks weird right? We are still missing some stuff. Let’s work on that next… we are missing just one line of code. Add the following modifier to the RowItem() inside the 2 functions:

.listRowBackground(Color.background)

And look what we’ve got now:

Isn’t that beautiful?

Let’s put the Navbar, Header and the HomeView together . In the HomeView, replace the createList() function call with the following:

VStack {
            HeaderView { filter in }
            createList()
        }

Here we’ve just added the HeaderView that we created earlier. Don’t worry about the closure, we will use it later on when we we start working with CoreData.

You should see this on the preview:

Now that you have put together the HeaderView with the List. We can now add the NavigationView. Go back in the ContentView, and you’ll find that the preview is not working no matter how many times you hit the resume button. Remember that we are passing the AuthenticationManager in the EnvironmentObject, but the preview does not have access to it, so we need to give it one. Replace the ContentView() in the preview:

let auth = AuthenticationManager()
auth.isLoggedIn = true
return ContentView().environmentObject(auth)

Here we are creating a new instance of the AuthenticationManager, and then we set the isLoggedIn to true explicitly to bypass the authentication screen.

Now if you resume the preview, everything should work as expected. However, you are still seeing the coming soon text. Let’s now replace it with the HomeView.

While you’re still in the ContentView.swift file, add the following property to the top of the file:

 @State private var showMenu = false

This property will be used in a later stage, we are adding now because it will be required shortly.

Now replace the coming soon text with the following:

VStack {
      NavBar(showMenu: self.$showMenu)
 }

This add the navigation bar we created earlier to the centre of the screen which is not what we want of course, we will be fixed shortly.

Below the NavBar in the VStack, add the following:

 HomeView()

The user interface is starting to come together, but there’re still some minor things we need to take care of.

Here is what we have now:

As you can see in the image above, we still need to fix those issues. Likely for us, it will only take exactly two lines to tidy up the UI, so add the following modifier to the ZStack container:

.edgesIgnoringSafeArea(.top)
            .background(Color.background)

Here is what those two do:

  1. The first modifier will push the User interface to the top and ignore the space at the top of the screen.
  2. The second just adds a background to the ZStack container

That’s it for this part guys, as I said I want to make the parts smaller so that I can explain some concepts a little bit better. If you have any question, send me an email. Don’t forget to subscribe if you haven’t done so already, and share this article. Happy coding!!!!

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