Everything done so far on the LoginView view.
This commit is contained in:
parent
bc17682898
commit
c584f19ec5
@ -7,6 +7,7 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
027F605B2937663400467238 /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 027F605A2937663400467238 /* LoginView.swift */; };
|
||||
02AE64EF29363DBF005A4AF3 /* BeRealApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AE64EE29363DBF005A4AF3 /* BeRealApp.swift */; };
|
||||
02AE64F129363DBF005A4AF3 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AE64F029363DBF005A4AF3 /* ContentView.swift */; };
|
||||
02AE64F329363DC1005A4AF3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 02AE64F229363DC1005A4AF3 /* Assets.xcassets */; };
|
||||
@ -14,6 +15,8 @@
|
||||
02AE650029363DC1005A4AF3 /* BeRealTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AE64FF29363DC1005A4AF3 /* BeRealTests.swift */; };
|
||||
02AE650A29363DC1005A4AF3 /* BeRealUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AE650929363DC1005A4AF3 /* BeRealUITests.swift */; };
|
||||
02AE650C29363DC1005A4AF3 /* BeRealUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AE650B29363DC1005A4AF3 /* BeRealUITestsLaunchTests.swift */; };
|
||||
02FFFD7829395D8C00306533 /* LoginForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FFFD7729395D8C00306533 /* LoginForm.swift */; };
|
||||
02FFFD7B29395DD200306533 /* String+Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FFFD7A29395DD200306533 /* String+Constants.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@ -34,6 +37,7 @@
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
027F605A2937663400467238 /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = "<group>"; };
|
||||
02AE64EB29363DBF005A4AF3 /* BeReal.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BeReal.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
02AE64EE29363DBF005A4AF3 /* BeRealApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BeRealApp.swift; sourceTree = "<group>"; };
|
||||
02AE64F029363DBF005A4AF3 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
@ -44,6 +48,8 @@
|
||||
02AE650529363DC1005A4AF3 /* BeRealUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BeRealUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
02AE650929363DC1005A4AF3 /* BeRealUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BeRealUITests.swift; sourceTree = "<group>"; };
|
||||
02AE650B29363DC1005A4AF3 /* BeRealUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BeRealUITestsLaunchTests.swift; sourceTree = "<group>"; };
|
||||
02FFFD7729395D8C00306533 /* LoginForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginForm.swift; sourceTree = "<group>"; };
|
||||
02FFFD7A29395DD200306533 /* String+Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Constants.swift"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@ -71,6 +77,23 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
027F60592937662300467238 /* Login */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
02FFFD7629395D7F00306533 /* Components */,
|
||||
027F605C29376EEB00467238 /* Views */,
|
||||
);
|
||||
path = Login;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
027F605C29376EEB00467238 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
027F605A2937663400467238 /* LoginView.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
02AE64E229363DBF005A4AF3 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -94,6 +117,8 @@
|
||||
02AE64ED29363DBF005A4AF3 /* BeReal */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
02FFFD7929395DBF00306533 /* Extensions */,
|
||||
027F60592937662300467238 /* Login */,
|
||||
02AE64EE29363DBF005A4AF3 /* BeRealApp.swift */,
|
||||
02AE64F029363DBF005A4AF3 /* ContentView.swift */,
|
||||
02AE64F229363DC1005A4AF3 /* Assets.xcassets */,
|
||||
@ -127,6 +152,22 @@
|
||||
path = BeRealUITests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
02FFFD7629395D7F00306533 /* Components */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
02FFFD7729395D8C00306533 /* LoginForm.swift */,
|
||||
);
|
||||
path = Components;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
02FFFD7929395DBF00306533 /* Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
02FFFD7A29395DD200306533 /* String+Constants.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
@ -192,6 +233,7 @@
|
||||
BuildIndependentTargetsInParallel = 1;
|
||||
LastSwiftUpdateCheck = 1410;
|
||||
LastUpgradeCheck = 1410;
|
||||
ORGANIZATIONNAME = "Röck+Cöde";
|
||||
TargetAttributes = {
|
||||
02AE64EA29363DBF005A4AF3 = {
|
||||
CreatedOnToolsVersion = 14.1;
|
||||
@ -257,8 +299,11 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
027F605B2937663400467238 /* LoginView.swift in Sources */,
|
||||
02AE64F129363DBF005A4AF3 /* ContentView.swift in Sources */,
|
||||
02AE64EF29363DBF005A4AF3 /* BeRealApp.swift in Sources */,
|
||||
02FFFD7829395D8C00306533 /* LoginForm.swift in Sources */,
|
||||
02FFFD7B29395DD200306533 /* String+Constants.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -17,6 +17,9 @@ struct ContentView: View {
|
||||
Text("Hello, world!")
|
||||
}
|
||||
.padding()
|
||||
.sheet(isPresented: .constant(true)) {
|
||||
LoginView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
11
BeReal/Extensions/String+Constants.swift
Normal file
11
BeReal/Extensions/String+Constants.swift
Normal file
@ -0,0 +1,11 @@
|
||||
//
|
||||
// String+Constants.swift
|
||||
// BeReal
|
||||
//
|
||||
// Created by Javier Cicchelli on 01/12/2022.
|
||||
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||
//
|
||||
|
||||
extension String {
|
||||
static let empty = ""
|
||||
}
|
92
BeReal/Login/Components/LoginForm.swift
Normal file
92
BeReal/Login/Components/LoginForm.swift
Normal file
@ -0,0 +1,92 @@
|
||||
//
|
||||
// LoginForm.swift
|
||||
// BeReal
|
||||
//
|
||||
// Created by Javier Cicchelli on 01/12/2022.
|
||||
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct LoginForm: View {
|
||||
|
||||
// MARK: Bindings
|
||||
|
||||
@Binding var username: String
|
||||
@Binding var password: String
|
||||
@Binding var errorMessage: String?
|
||||
|
||||
// MARK: Body
|
||||
|
||||
var body: some View {
|
||||
VStack(
|
||||
alignment: .leading,
|
||||
spacing: 16
|
||||
) {
|
||||
TextField(
|
||||
"Username",
|
||||
text: $username
|
||||
)
|
||||
.textContentType(.username)
|
||||
.autocapitalization(.none)
|
||||
.disableAutocorrection(true)
|
||||
.keyboardType(.default)
|
||||
|
||||
Divider()
|
||||
|
||||
SecureField(
|
||||
"Password",
|
||||
text: $password
|
||||
)
|
||||
.textContentType(.password)
|
||||
.autocapitalization(.none)
|
||||
.disableAutocorrection(true)
|
||||
.keyboardType(.default)
|
||||
|
||||
if let errorMessage {
|
||||
Divider()
|
||||
|
||||
Text(errorMessage)
|
||||
.font(.body)
|
||||
.foregroundColor(.red)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(16)
|
||||
.background(Color.white)
|
||||
.cornerRadius(8)
|
||||
.onAppear {
|
||||
setClearButtonIfNeeded()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
private extension LoginForm {
|
||||
func setClearButtonIfNeeded() {
|
||||
guard UITextField.appearance().clearButtonMode != .whileEditing else { return }
|
||||
|
||||
UITextField.appearance().clearButtonMode = .whileEditing
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct LoginForm_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
LoginForm(
|
||||
username: .constant("Some username"),
|
||||
password: .constant("Some Password"),
|
||||
errorMessage: .constant(nil)
|
||||
)
|
||||
.previewDisplayName("Login form with no error message")
|
||||
|
||||
LoginForm(
|
||||
username: .constant("Some username"),
|
||||
password: .constant("Some Password"),
|
||||
errorMessage: .constant("Some error goes in here...")
|
||||
)
|
||||
.previewDisplayName("Login form with some error message")
|
||||
}
|
||||
}
|
103
BeReal/Login/Views/LoginView.swift
Normal file
103
BeReal/Login/Views/LoginView.swift
Normal file
@ -0,0 +1,103 @@
|
||||
//
|
||||
// LoginView.swift
|
||||
// BeReal
|
||||
//
|
||||
// Created by Javier Cicchelli on 30/11/2022.
|
||||
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct LoginView: View {
|
||||
|
||||
// MARK: States
|
||||
|
||||
@State private var topPadding: CGFloat = 0
|
||||
|
||||
// MARK: Body
|
||||
|
||||
var body: some View {
|
||||
ScrollView(
|
||||
.vertical,
|
||||
showsIndicators: false
|
||||
) {
|
||||
Container()
|
||||
.padding(.horizontal, 24)
|
||||
.padding(.top, topPadding)
|
||||
}
|
||||
.background(Color.red)
|
||||
.ignoresSafeArea()
|
||||
.overlay(ViewHeightGeometry())
|
||||
.onPreferenceChange(ViewHeightPreferenceKey.self) { height in
|
||||
topPadding = height * 0.25
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Views
|
||||
|
||||
fileprivate extension LoginView {
|
||||
struct Container: View {
|
||||
@State private var username: String = .empty
|
||||
@State private var password: String = .empty
|
||||
@State private var errorMessage: String?
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 24) {
|
||||
Text("My NFS")
|
||||
.font(.largeTitle)
|
||||
.fontWeight(.bold)
|
||||
|
||||
LoginForm(
|
||||
username: $username,
|
||||
password: $password,
|
||||
errorMessage: $errorMessage
|
||||
)
|
||||
|
||||
Button {
|
||||
// ...
|
||||
} label: {
|
||||
Text("Log in")
|
||||
.font(.body)
|
||||
.fontWeight(.semibold)
|
||||
.padding(.vertical, 8)
|
||||
.padding(.horizontal, 32)
|
||||
.background(
|
||||
Capsule()
|
||||
.foregroundColor(.white)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ViewHeightGeometry: View {
|
||||
var body: some View {
|
||||
GeometryReader { proxy in
|
||||
Color.clear.preference(
|
||||
key: ViewHeightPreferenceKey.self,
|
||||
value: proxy.size.height + proxy.safeAreaInsets.top + proxy.safeAreaInsets.bottom
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Preference keys
|
||||
|
||||
struct ViewHeightPreferenceKey: PreferenceKey {
|
||||
static var defaultValue: CGFloat = 0
|
||||
|
||||
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
|
||||
value = nextValue()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct LoginView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
LoginView()
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user