diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..a9e7eb9 Binary files /dev/null and b/.DS_Store differ diff --git a/SwiftUIBasics.xcodeproj/project.pbxproj b/SwiftUIBasics.xcodeproj/project.pbxproj index f0adfa8..24789b3 100644 --- a/SwiftUIBasics.xcodeproj/project.pbxproj +++ b/SwiftUIBasics.xcodeproj/project.pbxproj @@ -22,6 +22,7 @@ 5598D3222B2403CF00A6AFD3 /* CircularImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5598D3212B2403CE00A6AFD3 /* CircularImage.swift */; }; 5598D3242B24049B00A6AFD3 /* ProfileCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5598D3232B24049B00A6AFD3 /* ProfileCardView.swift */; }; 5598D3262B2404FC00A6AFD3 /* ProfileCardVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5598D3252B2404FC00A6AFD3 /* ProfileCardVM.swift */; }; + E63894902B2D0EF200B7D57C /* CatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E638948F2B2D0EF200B7D57C /* CatsView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -41,6 +42,7 @@ 5598D3212B2403CE00A6AFD3 /* CircularImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircularImage.swift; sourceTree = ""; }; 5598D3232B24049B00A6AFD3 /* ProfileCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileCardView.swift; sourceTree = ""; }; 5598D3252B2404FC00A6AFD3 /* ProfileCardVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileCardVM.swift; sourceTree = ""; }; + E638948F2B2D0EF200B7D57C /* CatsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CatsView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -73,6 +75,7 @@ 5570428C2B24D8050048C81C /* SignUpView.swift */, 5570428E2B24E50E0048C81C /* RatingView.swift */, 557042902B24E5220048C81C /* PlansView.swift */, + E638948F2B2D0EF200B7D57C /* CatsView.swift */, ); path = Views; sourceTree = ""; @@ -203,6 +206,7 @@ 5598D3242B24049B00A6AFD3 /* ProfileCardView.swift in Sources */, 557042952B24F3100048C81C /* RequirementText.swift in Sources */, 557042932B24EFB00048C81C /* FormTextField.swift in Sources */, + E63894902B2D0EF200B7D57C /* CatsView.swift in Sources */, 557042892B24CB310048C81C /* ProfileListView.swift in Sources */, 557042912B24E5220048C81C /* PlansView.swift in Sources */, 5570428D2B24D8050048C81C /* SignUpView.swift in Sources */, diff --git a/SwiftUIBasics.xcodeproj/project.xcworkspace/xcuserdata/gustavoali.xcuserdatad/UserInterfaceState.xcuserstate b/SwiftUIBasics.xcodeproj/project.xcworkspace/xcuserdata/gustavoali.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..c070f3b Binary files /dev/null and b/SwiftUIBasics.xcodeproj/project.xcworkspace/xcuserdata/gustavoali.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/SwiftUIBasics.xcodeproj/xcuserdata/gustavoali.xcuserdatad/xcschemes/xcschememanagement.plist b/SwiftUIBasics.xcodeproj/xcuserdata/gustavoali.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..27cd9e4 --- /dev/null +++ b/SwiftUIBasics.xcodeproj/xcuserdata/gustavoali.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + SwiftUIBasics.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/SwiftUIBasics/SwiftUIBasicsApp.swift b/SwiftUIBasics/SwiftUIBasicsApp.swift index 194f2b3..35bd39f 100644 --- a/SwiftUIBasics/SwiftUIBasicsApp.swift +++ b/SwiftUIBasics/SwiftUIBasicsApp.swift @@ -11,7 +11,7 @@ import SwiftUI struct SwiftUIBasicsApp: App { var body: some Scene { WindowGroup { - ContentView() + SignUpView() } } } diff --git a/SwiftUIBasics/Views/CatsView.swift b/SwiftUIBasics/Views/CatsView.swift new file mode 100644 index 0000000..2ddcd96 --- /dev/null +++ b/SwiftUIBasics/Views/CatsView.swift @@ -0,0 +1,22 @@ +// +// CatsView.swift +// SwiftUIBasics +// +// Created by Gustavo Ali Gómez Trejo on 15/12/23. +// + +import SwiftUI + +struct CatsView: View { + var body: some View { + List { + ForEach((1..<20)){ _ in + + } + } + } +} + +#Preview { + CatsView() +} diff --git a/SwiftUIBasics/Views/Components/CatsView.swift b/SwiftUIBasics/Views/Components/CatsView.swift new file mode 100644 index 0000000..f8c7a0e --- /dev/null +++ b/SwiftUIBasics/Views/Components/CatsView.swift @@ -0,0 +1,8 @@ +// +// CatsView.swift +// SwiftUIBasics +// +// Created by Gustavo Ali Gómez Trejo on 15/12/23. +// + +import Foundation diff --git a/SwiftUIBasics/Views/PlansView.swift b/SwiftUIBasics/Views/PlansView.swift index cb97876..14b97bc 100644 --- a/SwiftUIBasics/Views/PlansView.swift +++ b/SwiftUIBasics/Views/PlansView.swift @@ -7,12 +7,92 @@ import SwiftUI -struct PlansView: View { +struct ContentView2: View { var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + VStack(alignment:.center){ + Text("Choose") + .font(.system(size: 32, weight: .black, design: .serif)) + .fontDesign(.rounded) + Text("Your Plan") + .font(.system(size: 32, weight: .black, design: .serif)) + .fontDesign(.rounded) + HStack { + CardView(iconName: "", title: "Basic", description: "Per month", price: "$9") + .foregroundStyle(.white) + .background(.purple) + .cornerRadius(6.0) + ZStack { + CardView(iconName: "", title: "Pro", description: "Per month", price: "$19") + .background(.gray) + .cornerRadius(6.0) + BadgeView(text: "Best for designer", padding: 5) + .foregroundStyle(.white) + .background(.orange) + .padding(.top, 200) + } + } + VStack { + CardView(iconName: "sparkles", title: "Team", description: " Per month ", price: "299") + .background(.black) + .foregroundStyle(.white) + .cornerRadius(6.0) + BadgeView(text: "Perfect for teams with 20 memebers", padding: 5) + .foregroundStyle(.white) + .background(.orange) + .padding(.top, -30) + + } + } + + } } + #Preview { - PlansView() + ContentView2() +} + +struct CardView: View { + var iconName: String + var title: String + var description: String + var price: String + + var body: some View { + VStack(spacing: 10) { + Image(systemName: iconName) + .font(.system(size: 40)) + .padding(.top, 30) + .padding(.bottom, -15) + VStack(spacing: 10) { + Text(title) + .font(.system(.title, design: .rounded)) + .fontWeight(.black) + .padding(.horizontal) + .padding(.vertical) + .padding(.bottom, -25) + Text(price) + .fontWeight(.bold) + .font(.system(size: 40, design: .rounded)) + Text(description) + .padding(.horizontal,40) + .padding(.vertical) + .padding(.bottom) + } + } + } + +} + +struct BadgeView: View { + var text: String + var padding: CGFloat + + + var body: some View { + Text(text) + .padding(padding) + + } } diff --git a/SwiftUIBasics/Views/SignUpView.swift b/SwiftUIBasics/Views/SignUpView.swift index 6cef5d9..b04c47b 100644 --- a/SwiftUIBasics/Views/SignUpView.swift +++ b/SwiftUIBasics/Views/SignUpView.swift @@ -10,26 +10,36 @@ 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 isValidEmailLength: Bool = false @Published var isValidPasswordLength: Bool = false @Published var isValidPasswordUpperCase: Bool = false + @Published var isValidPasswordLowerCase: Bool = false + @Published var isValidPasswordSymbol: Bool = false + @Published var isValidPasswordNumber: Bool = false @Published var isValidPasswordMatch: Bool = false @Published var isValid: Bool = false + @Published var isValidPre: Bool = false + private var cancelableSet: Set = [] init() { - $username + $email .receive(on: RunLoop.main) - .map { username in - return username.count >= 4 + .map { email in + let pattern = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}" + if let _ = email.range(of: pattern, options: .regularExpression) { + return true + } else { + return false + } } - .assign(to: \.isValidUsernameLength, on: self) + .assign(to: \.isValidEmailLength, on: self) .store(in: &cancelableSet) $password @@ -52,7 +62,43 @@ class SignUpViewModel: ObservableObject { } .assign(to: \.isValidPasswordUpperCase, 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 + } + } + .assign(to: \.isValidPasswordLowerCase, on: self) + .store(in: &cancelableSet) + $password + .receive(on: RunLoop.main) + .map { password in + let pattern = "[0-9]" + if let _ = password.range(of: pattern, options: .regularExpression) { + return true + } else { + return false + } + } + .assign(to: \.isValidPasswordNumber, on: self) + .store(in: &cancelableSet) + $password + .receive(on: RunLoop.main) + .map { password in + let pattern = "[!\"#$%&'()*+,-./:;<=>?@\\[\\\\\\]^_`{|}~]+" + if let _ = password.range(of: pattern, options: .regularExpression) { + return true + } else { + return false + } + } + .assign(to: \.isValidPasswordSymbol, on: self) + .store(in: &cancelableSet) + Publishers.CombineLatest($password, $passwordConfirm) .receive(on: RunLoop.main) .map { (password, passwordConfirm) in @@ -60,8 +106,15 @@ class SignUpViewModel: ObservableObject { } .assign(to: \.isValidPasswordMatch, on: self) .store(in: &cancelableSet) + + Publishers.CombineLatest4($isValidPasswordNumber, $isValidPasswordSymbol, $isValidPasswordLowerCase, $isValidPasswordUpperCase) + .map { (a, b, c, d) in + return a && b && c && d + } + .assign(to: \.isValidPre, on: self) + .store(in: &cancelableSet) - Publishers.CombineLatest4($isValidUsernameLength, $isValidPasswordLength, $isValidPasswordUpperCase, $isValidPasswordMatch) + Publishers.CombineLatest4($isValidEmailLength, $isValidPasswordLength, $isValidPre, $isValidPasswordMatch) .map { (a, b, c, d) in return a && b && c && d } @@ -80,13 +133,18 @@ struct SignUpView: View { .bold() .foregroundStyle(.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) + .autocapitalization(.none) + RequirementText(text: "Enter a valid Email.", isValid: vm.isValidEmailLength) .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: "One lowercase letter", isValid: vm.isValidPasswordLowerCase) + RequirementText(text: "One symbol", isValid: vm.isValidPasswordSymbol) + RequirementText(text: "One number", isValid: vm.isValidPasswordNumber) } .padding() FormTextField(name: "Confirm Password", value: $vm.passwordConfirm, isSecure: true)