Zara replica - Part 1
Getting started & Login Screen
This is part one of many in which we are going to create an e-commerce app inspired by the ZARA mobile app…
At WWDC 20, apple said that it is now possible to build an entire app in swift UI… Although this statement is valid for the majority of apps, there are still features and functionalities that need the power of UIKit to be implemented correctly. In my humble opinion, UIKit is still relevant to this day, and it will not disappear anytime soon. So if you want to be a flexible iOS developer, you will need to know both frameworks.
In this series, we will use a bit of UIKit to implement a feature that would be hard to implement with only swift UI.
To follow along, you need:
- Xcode 12 or greater
- MacOS Catalina or greater
- Swift 5.0 or greater
- iOS 14 or greater
- Open Xcode
- Create a new Xcode project
- Select App and click next
- Name your app and make sure the user interface is Swift UI
- Last, click Finish.
Or you can get the starter project at https://liquidcoder.com/course/zara-replica , you will need a free account to get it.
Let’s first create a bunch of folders and files that we will use throughout this series, and we will keep adding on them as we go.
To follow along, you need download the starter project source code linked above, and when you finish, try to compare with the final project.
Create the following folders in your root folder: Views, Utils, Extensions, Data, Screens, UIKit
Extensions
Add the following in the Extensions folder:
Color+.swift
import SwiftUI
extension Color {
static var background = Color("background")
static var text = Color("text")
}
The above code will work if you’ve downloaded the initial project code, otherwise you will need to add the 2 colors in the Assets.xcassets folder.
View+.swift
import SwiftUI
extension View {
func thinFont() -> some View {
self.font(.system(size: 20, weight: Font.Weight.thin, design: Font.Design.default))
}
func lightFont() -> some View {
self.font(.system(size: 12, weight: Font.Weight.light, design: Font.Design.default))
}
func boldFont() -> some View {
self.font(.system(size: 15, weight: Font.Weight.bold, design: Font.Design.default))
}
func textColor() -> some View {
self.foregroundColor(.text)
}
func reverseTextColor() -> some View {
self.foregroundColor(.background)
}
func bgColor() -> some View {
self.background(Color.background)
}
func reverseBgColor() -> some View {
self.background(Color.text)
}
}
In case you are not familiar with extensions in swift, I will link some resources to help you out. In short, this will allow us to call the above functions as modifiers, because they are modifiers. We might add more files into this folder as we go.
Buttons
Now, in the Views folder, add another folder named Buttons. Next, add the following in that folder. The following are different styles of buttons we will use throughout this series.
CloseButton.swift
import SwiftUI
struct CloseButton: View {
var onDismiss = {}
var body: some View {
Button(action: onDismiss, label: {
Image(systemName: "xmark")
.thinFont()
.textColor()
})
}
}
struct CloseButton_Previews: PreviewProvider {
static var previews: some View {
CloseButton()
}
}
BorderedButton.swift
struct BorderedButton: View {
var text: String
var action = {}
var body: some View {
Button(action: action, label: {
Text(text)
.boldFont()
.textColor()
.padding(.horizontal, 30)
.padding(.vertical, 10)
.border(Color.text, width: 1)
})
}
}struct BorderedButton_Previews: PreviewProvider {
static var previews: some View {
BorderedButton(text: "ADD")
}
}
Preview
IconButton.swift
struct IconButton: View {
var icon: String
var action = {}
var body: some View {
Button(action: action, label: {
Image(systemName: icon)
.lightFont()
.textColor()
})
}
}struct IconButton_Previews: PreviewProvider {
static var previews: some View {
IconButton(icon: "square.and.arrow.up")
}
}
Preview
FilledButton.swift
import SwiftUIstruct FilledButton: View {
var text: String
var action = {}
var body: some View {
Button(action: action, label: {
Text(text)
.boldFont()
.reverseTextColor()
.padding(.horizontal, 30)
.padding(.vertical, 10)
})
}
}struct FilledButton_Previews: PreviewProvider {
static var previews: some View {
FilledButton(text: "LOG IN")
.reverseBgColor()
}
}
Preview
NakedButton.swift
import SwiftUIstruct NakedButton: View {
var text: String
var alignment: Alignment = .center
var action = {}
var body: some View {
Button(action: action, label: {
Text(text)
.lightFont()
.textColor()
.padding(.vertical, 10)
.frame(maxWidth: .infinity, alignment: alignment)
})
}
}struct NakedButton_Previews: PreviewProvider {
static var previews: some View {
NakedButton(text: "Have you forgotten your password?")
}
}
Preview
LargeNakedButton.swift
import SwiftUI
struct LargeNakedButton: View {
var text: String
var action = {}
var body: some View {
Button(action: action, label: {
Text(text)
.font(.system(size: 30, weight: Font.Weight.black, design: Font.Design.default))
.foregroundColor(.text)
.frame(maxWidth: .infinity, alignment: .leading)
})
}
}
struct LargeNakedButton_Previews: PreviewProvider {
static var previews: some View {
LargeNakedButton(text: "MY INFORMATION")
}
}
Preview
In the Screens folder, add the following:
Login Screen
In the Screens folder, add the following:
LoginScreen.swift
struct LoginScreen: View {
var onDismiss = {}
@State private var email = ""
@State private var password = ""
var body: some View { // 1
VStack{
// 2
HStack {
CloseButton(action: onDismiss)
Spacer()
}
Spacer()
// 3
VStack(spacing: 20) {
VStack {
TextField("Email", text: self.$email)
Divider()
}
VStack {
SecureField("Password", text: self.$email)
Divider()
}
FilledButton(text: "LOG IN", action: onDismiss)
.frame(maxWidth: .infinity)
.reverseBgColor()
NakedButton(text: "Have you forgotten your password?")
}
Spacer()
// 4
VStack{
BorderedButton(text: "CHAT", action: {})
NakedButton(text: "Don't have an account? Register")
}
}.padding()
}
}struct LoginScreen_Previews: PreviewProvider {
static var previews: some View {
LoginScreen()
}
}
Explanation
- I first a declare a closure that will be called when the onDismiss the close button or the login button are tapped. I also declare 2 state properties that will hold the email and password.
- I put everything inside a
VStack
because the UI will flow from top to bottom. - We then create the horizontal invisible navbar that will only contain the
CloseButton
and aSpacer
. TheSpacer
is used to push theCloseButton
to the left edge of the screen. - Next, We create the form inside a
VStack
again , and surround it with 2Spacer
to center it vertically. With swiftUI 2.0, there's no need to handle the keyboard as the form will get out of the way when one starts typing. - Last, we create the footer which is a simple
VStack
That’s it for this part one. As you may have noticed, this part was mainly preparation and house keeping, stay tuned for second part in which we will dive deeper into TabView
and Paging
like the tik tok app. Subscribe to our newsletter if you haven't done so already and share this article.
Checkout a best version of this article at https://liquidcoder.com.