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..4754bfe 100644 --- a/SwiftUIBasics/Views/RatingView.swift +++ b/SwiftUIBasics/Views/RatingView.swift @@ -1,18 +1,30 @@ -// -// RatingView.swift -// SwiftUIBasics -// -// Created by Diplomado on 09/12/23. -// - import SwiftUI +struct StarRatingView: View { + @State private var rating: Int = 0 -struct RatingView: View { 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() + } } +