diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..033d02f
Binary files /dev/null and b/.DS_Store differ
diff --git a/SwiftUIBasics.xcodeproj/project.xcworkspace/xcuserdata/anapaulaflores.xcuserdatad/UserInterfaceState.xcuserstate b/SwiftUIBasics.xcodeproj/project.xcworkspace/xcuserdata/anapaulaflores.xcuserdatad/UserInterfaceState.xcuserstate
new file mode 100644
index 0000000..8ff3dc2
Binary files /dev/null and b/SwiftUIBasics.xcodeproj/project.xcworkspace/xcuserdata/anapaulaflores.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/SwiftUIBasics.xcodeproj/xcuserdata/anapaulaflores.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/SwiftUIBasics.xcodeproj/xcuserdata/anapaulaflores.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
new file mode 100644
index 0000000..17be66e
--- /dev/null
+++ b/SwiftUIBasics.xcodeproj/xcuserdata/anapaulaflores.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/SwiftUIBasics.xcodeproj/xcuserdata/anapaulaflores.xcuserdatad/xcschemes/xcschememanagement.plist b/SwiftUIBasics.xcodeproj/xcuserdata/anapaulaflores.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 0000000..27cd9e4
--- /dev/null
+++ b/SwiftUIBasics.xcodeproj/xcuserdata/anapaulaflores.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,14 @@
+
+
+
+
+ SchemeUserState
+
+ SwiftUIBasics.xcscheme_^#shared#^_
+
+ orderHint
+ 0
+
+
+
+
diff --git a/SwiftUIBasics/Views/PlansView.swift b/SwiftUIBasics/Views/PlansView.swift
index cb97876..f76ebae 100644
--- a/SwiftUIBasics/Views/PlansView.swift
+++ b/SwiftUIBasics/Views/PlansView.swift
@@ -9,9 +9,93 @@ import SwiftUI
struct PlansView: View {
var body: some View {
- Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
- }
-}
+
+ VStack {
+ VStack (alignment: .leading, spacing: 2) {
+ Text("Choose")
+ .font(.system(.largeTitle, design: .rounded))
+ .fontWeight(.black)
+ Text("Your Plan")
+ .font(.system(.largeTitle, design: .rounded))
+ .fontWeight(.black)
+ }
+
+ HStack {
+ ZStack {
+ RoundedRectangle(cornerRadius: 10)
+ .foregroundColor(.purple)
+ .frame(height: 200)
+ VStack {
+ Text("Basic")
+ .bold()
+ .font(.system(.title, design: .rounded))
+ Text("$9")
+ .bold()
+ .font(.system(size: 40, weight: .heavy, design: .rounded))
+ Text("per month")
+ .font(.headline)
+ }
+ .foregroundColor(Color.white)
+ }
+
+ ZStack {
+ RoundedRectangle(cornerRadius: 10)
+ .foregroundColor(Color(red: 240/255, green: 240/255, blue: 240/255))
+ .frame(height: 200)
+ VStack {
+ Text("Pro")
+ .bold()
+ .font(.system(.title, design: .rounded))
+ Text("$19")
+ .bold()
+ .font(.system(size: 40, weight: .heavy, design: .rounded))
+ Text("per month")
+ .font(.headline)
+ }
+ .foregroundColor(Color.black)
+
+ Text("Best for designer")
+ .font(.system(.caption, design: .rounded))
+ .fontWeight(.bold)
+ .foregroundColor(.white)
+ .padding(5)
+ .background(Color(red: 255/255, green: 183/255, blue: 37/255))
+ .offset(x: 0, y: 98)
+ }
+ }
+ .padding()
+
+ ZStack {
+ RoundedRectangle(cornerRadius: 10)
+ .foregroundColor(Color(red: 62/255, green: 63/255, blue: 70/255))
+ .frame(width: 360, height: 222)
+ VStack {
+ Image(systemName: "wand.and.rays")
+ .font(.largeTitle)
+ Text("Team")
+ .bold()
+ .font(.system(.title, design: .rounded))
+
+ Text("$299")
+ .bold()
+ .font(.system(size: 40, weight: .heavy, design: .rounded))
+ Text("per month")
+ .font(.headline)
+ }
+ .foregroundColor(Color.white)
+
+ Text("Perfect for teams with 20 members")
+ .font(.system(.caption, design: .rounded))
+ .fontWeight(.bold)
+ .foregroundColor(.white)
+ .padding(5)
+ .background(Color(red: 255/255, green: 183/255, blue: 37/255))
+ .offset(x: 0, y: 110)
+ }
+ }
+ }
+ }
+
#Preview {
PlansView()
diff --git a/SwiftUIBasics/Views/RatingView.swift b/SwiftUIBasics/Views/RatingView.swift
index 3ca74af..c6f447a 100644
--- a/SwiftUIBasics/Views/RatingView.swift
+++ b/SwiftUIBasics/Views/RatingView.swift
@@ -1,18 +1,27 @@
-//
-// RatingView.swift
-// SwiftUIBasics
-//
-// Created by Diplomado on 09/12/23.
-//
-
import SwiftUI
-
-struct RatingView: View {
+struct StarRatingView: View {
+ @State private var rating: Int = 0
var body: some View {
- Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
+ VStack {
+ Text("Selected Rating: \(rating)")
+ HStack {
+ ForEach(1...5, id: \.self) { index in
+ Image(systemName: index <= self.rating ? "star.fill" : "star")
+ .resizable()
+ .frame(width: 40, height: 40)
+ .foregroundColor(.yellow)
+ .onTapGesture {
+ self.rating = index
+ }
+ }
+ }
+ }
+ .padding()
}
}
-
-#Preview {
- RatingView()
+struct ContentView_Previews: PreviewProvider {
+ static var previews: some View {
+ StarRatingView()
+ }
}
+
diff --git a/SwiftUIBasics/Views/SignUpView.swift b/SwiftUIBasics/Views/SignUpView.swift
index 6cef5d9..5bb4978 100644
--- a/SwiftUIBasics/Views/SignUpView.swift
+++ b/SwiftUIBasics/Views/SignUpView.swift
@@ -1,56 +1,37 @@
-//
-// SignUpView.swift
-// SwiftUIBasics
-//
-// Created by Diplomado on 09/12/23.
-//
-
import SwiftUI
import Combine
class SignUpViewModel: ObservableObject {
// inputs
- @Published var username: String = ""
+ @Published var email: String = ""
@Published var password: String = ""
@Published var passwordConfirm: String = ""
// outputs
- @Published var isValidUsernameLength: Bool = false
- @Published var isValidPasswordLength: Bool = false
- @Published var isValidPasswordUpperCase: Bool = false
+ @Published var isValidEmail: Bool = false
+ @Published var isValidPassword: Bool = false
@Published var isValidPasswordMatch: Bool = false
@Published var isValid: Bool = false
private var cancelableSet: Set = []
init() {
- $username
- .receive(on: RunLoop.main)
- .map { username in
- return username.count >= 4
- }
- .assign(to: \.isValidUsernameLength, on: self)
- .store(in: &cancelableSet)
-
- $password
+ $email
.receive(on: RunLoop.main)
- .map { password in
- return password.count >= 8
+ .map { email in
+ let emailPattern = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
+ let emailPredicate = NSPredicate(format: "SELF MATCHES %@", emailPattern)
+ return emailPredicate.evaluate(with: email)
}
- .assign(to: \.isValidPasswordLength, on: self)
+ .assign(to: \.isValidEmail, on: self)
.store(in: &cancelableSet)
$password
.receive(on: RunLoop.main)
.map { password in
- let pattern = "[A-Z]"
- if let _ = password.range(of: pattern, options: .regularExpression) {
- return true
- } else {
- return false
- }
+ return password.isValidPassword()
}
- .assign(to: \.isValidPasswordUpperCase, on: self)
+ .assign(to: \.isValidPassword, on: self)
.store(in: &cancelableSet)
Publishers.CombineLatest($password, $passwordConfirm)
@@ -61,9 +42,9 @@ class SignUpViewModel: ObservableObject {
.assign(to: \.isValidPasswordMatch, on: self)
.store(in: &cancelableSet)
- Publishers.CombineLatest4($isValidUsernameLength, $isValidPasswordLength, $isValidPasswordUpperCase, $isValidPasswordMatch)
- .map { (a, b, c, d) in
- return a && b && c && d
+ Publishers.CombineLatest3($isValidEmail, $isValidPassword, $isValidPasswordMatch)
+ .map { (a, b, c) in
+ return a && b && c
}
.assign(to: \.isValid, on: self)
.store(in: &cancelableSet)
@@ -78,21 +59,28 @@ struct SignUpView: View {
Text("Create an account")
.font(.system(.largeTitle, design: .rounded))
.bold()
- .foregroundStyle(.maryBlue)
+ .foregroundColor(.maryBlue)
.padding(.bottom, 30)
- FormTextField(name: "Username", value: $vm.username)
- RequirementText(text: "A minimum of 4 characters", isValid: vm.isValidUsernameLength)
+ FormTextField(name: "Email", value: $vm.email)
+ .keyboardType(.emailAddress)
+ RequirementText(text: "Valid email format", isValid: vm.isValidEmail)
.padding()
+
FormTextField(name: "Password", value: $vm.password, isSecure: true)
VStack {
- RequirementText(text: "A minimum of 8 characters", isValid: vm.isValidPasswordLength)
- RequirementText(text: "One uppercase letter", isValid: vm.isValidPasswordUpperCase)
+ RequirementText(text: "At least 8 characters", isValid: vm.isValidPassword)
+ RequirementText(text: "At least one uppercase letter", isValid: vm.password.containsUppercase())
+ RequirementText(text: "At least one lowercase letter", isValid: vm.password.containsLowercase())
+ RequirementText(text: "At least one symbol", isValid: vm.password.containsSymbol())
+ RequirementText(text: "At least one number", isValid: vm.password.containsNumber())
}
.padding()
+
FormTextField(name: "Confirm Password", value: $vm.passwordConfirm, isSecure: true)
RequirementText(text: "Your confirm password should be the same as password", isValid: vm.isValidPasswordMatch)
.padding()
.padding(.bottom, 50)
+
Button(action: {
print("Doing")
// Proceed to the next screen
@@ -104,7 +92,6 @@ struct SignUpView: View {
.padding()
.frame(minWidth: 0, maxWidth: .infinity)
.background(vm.isValid ? .maryBlue :.turquoise)
- // .background(LinearGradient(gradient: Gradient(colors: [.turquoise, .maryBlue]), startPoint: .leading, endPoint: .trailing))
.cornerRadius(10)
.padding(.horizontal)
}
@@ -129,6 +116,33 @@ struct SignUpView: View {
}
}
-#Preview {
- SignUpView()
+extension String {
+ func isValidPassword() -> Bool {
+ // Implement your password validation logic here
+ return self.count >= 8
+ }
+
+ func containsUppercase() -> Bool {
+ return self.rangeOfCharacter(from: .uppercaseLetters) != nil
+ }
+
+ func containsLowercase() -> Bool {
+ return self.rangeOfCharacter(from: .lowercaseLetters) != nil
+ }
+
+ func containsSymbol() -> Bool {
+ let symbolCharacterSet = CharacterSet(charactersIn: "!@#$%^&*()-_=+[]{}|;:'\",.<>/?`~")
+ return self.rangeOfCharacter(from: symbolCharacterSet) != nil
+ }
+
+ func containsNumber() -> Bool {
+ return self.rangeOfCharacter(from: .decimalDigits) != nil
+ }
+}
+
+struct SignUpView_Previews: PreviewProvider {
+ static var previews: some View {
+ SignUpView()
+ }
}
+