Sign Up validation Instagram (iOS) - validation

I was given a signUp validation task. in doing so I tried my best. but I would like you to offer a better way or suggestion.
exception: Due to the correct email format, doSignUp () [Home page] is logged, even if the password format is incorrect
thanks for the attention
this is a view of my attempts 👇
#State var fullname = "Vignesh"
#State var email = "pdponline999#gmail.com"
#State var password = ""
#State var cpassword = ""
#State var showError1 = false
#State var showError2 = false
#State var shouldHide = false
let emailFormat = NSPredicate(format: "SELF MATCHES %#", "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}")
let passwordFormat = NSPredicate(format: "SELF MATCHES %#", "^(?=.*[A-Z])(?=.*[a-z])(?=.*?[0-9])(?=.*[$#$#!%*?&])[A-Za-z\\d$#$#!%*?&]{8,}")
TextField and SecureField
VStack(alignment: .leading) {
TextField("email", text: $email)
.frame(height: 45)
.padding(.leading)
.background(.white.opacity(0.4))
.cornerRadius(10)
.textInputAutocapitalization(.never)
if showError1 == true {
Text("Email is Not Valid")
.font(.footnote)
.fontWeight(.semibold)
.foregroundColor(.red)
.opacity(shouldHide ? 0 : 1)
}
}
VStack(alignment: .leading) {
SecureField("password", text: $password)
.frame(height: 45)
.padding(.leading)
.background(.white.opacity(0.4))
.cornerRadius(10)
if showError2 == true {
Text("Password is Not Valid")
.font(.footnote)
.fontWeight(.semibold)
.foregroundColor(.red)
.opacity(shouldHide ? 0 : 1)
}
}
Sign Up button
Button(action: {
if !emailFormat.evaluate(with: email) {
print("Check your email. And try again !!")
self.showError1 = true
self.shouldHide = false
} else {
self.showError1 = false
self.shouldHide = true
doSignUp()
}
if !passwordFormat.evaluate(with: password) {
print("Check your password. And try again !!")
self.showError2 = true
self.shouldHide = false
} else {
self.showError2 = false
self.shouldHide = true
doSignUp()
}
}, label: {
Text("sign_up")
.frame(maxWidth: .infinity).frame(height: 50)
.foregroundColor(.white)
.background(RoundedRectangle(cornerRadius: 10)
.stroke(lineWidth: 1.5)
.foregroundColor(.white.opacity(0.4)))
})

Related

VStack to the center of the screen

I have to display like the following , used three VStacks to display it. However the login VStack is to the bottom of the screen. How to place it to the center of the screen and Google image to the top most of the screen
struct Login: View {
var body: some View {
ZStack{
CustomNavigationBar() // To diplay image
VStack{
LoginView(lgnvwmmodel: lgnvwmmodel) //To display Login View
HelpView()
}
}
}
}
struct CustomNavigationBar: View{
var body: some View{
NavigationView{
Image(uiImage: UIImage(named: "google.png")!)
} .navigationViewStyle(StackNavigationViewStyle())
.navigationBarTitleDisplayMode(.inline)
.padding(0)
}
}
struct LoginView: View {
#State private var emailId: String = ""
#State private var password: String = ""
#State var ntwrkShowAlert = false
#State private var success = false
#State var isModal:Bool = false
#EnvironmentObject var DataGetting : DataStorage
#ObservedObject var monitor = NetworkMonitor()
#State private var isEmailValid : Bool = true
#State private var showingAlert = false
#State var chngpasscode = false
#State var btnClicked:CancelClicked = .none
#State var showPopup = false
#ObservedObject var lgnvwmmodel : LoginViewModel
var body: some View {
Text("Login")
.font(.custom("OpenSans-Bold", size: 24))
.padding(.top, 100)
TextField("Email ID", text: $emailId)
.onReceive(Just(emailId), perform: { _ in
if emailId.count >= 50{
emailId = String(emailId.prefix(50))
}
})
.modifier(customViewModifier(roundedCornes: 6, textColor: .black))
.frame(height: 100)
.padding([.leading, .trailing], 100)
SecureField("Password", text: $password)
.onReceive(Just(password), perform: { _ in
if password.count >= 50{
password = String(password.prefix(50))
}
})
.modifier(customViewModifier(roundedCornes: 6, textColor: .black))
.frame(height: 100)
.padding([.leading, .trailing], 100)
Button(action: {
}, label: {
Text("Forgot Password ?")
.underline()
.frame( height: 40, alignment: .trailing)
.padding(.leading, 320)
})
Button(action: {
if(DataGetting.strEmail == ""){
UserDefaults.standard.setValue(self.emailId, forKey:"Email")
}
if((DataGetting.strPassword) == 0){
UserDefaults.standard.setValue(self.password, forKey: "Password")
}
self.isModal = true
if(monitor.status == .connected){
self.ntwrkShowAlert = false
lgnvwmmodel.dataStorage = DataGetting
lgnvwmmodel.getUserIdDetails(emailId: emailId, password: password){ status in
do {
if(status){
success = true
}
}
}
}
else{
self.ntwrkShowAlert = true
}
}, label: {
Text("Sign In")
.frame(width: 200, height: 30)
.padding()
.background(Color.red)
.foregroundColor(Color.white)
.font(.custom("OpenSans-Bold", size: 28))
}).alert(isPresented: $ntwrkShowAlert){
return Alert(title: Text("Please check your internet connection"), message: Text(""), dismissButton: .default(Text("OK")))
}
}
}
struct HelpView: View{
#State private var isAlert = false
#State private var errorMessage: String = ""
var body: some View{
Button(action: {
}, label: {
Text("Help")
.frame(minWidth: 0, maxWidth: .infinity, maxHeight: 80)
.background(Color.red)
.foregroundColor(Color.white)
.font(.custom("OpenSans-Bold", size: 28))
}).alert(isPresented: $isAlert){
Alert(title: Text(self.errorMessage), message: Text(""), dismissButton: .default(Text("OK")))
}
}
}
Here is a fixed simplified version (without models or button actions). I'm not sure why you wrapped the Google logo in a NavigationView? – so I skipped it as it causes layout issues.
struct ContentView: View {
var body: some View {
VStack{
CustomNavigationBar()
Spacer()
LoginView()
Spacer()
HelpView()
}
}
}
struct CustomNavigationBar: View{
var body: some View{
Image("google")
}
}
struct LoginView: View {
#State private var emailId: String = ""
#State private var password: String = ""
var body: some View {
Text("Login")
.font(.custom("OpenSans-Bold", size: 24))
TextField("Email ID", text: $emailId)
.padding().border(.black, width: 1)
.frame(height: 100)
.padding([.leading, .trailing], 100)
SecureField("Password", text: $password)
.padding().border(.black, width: 1)
.frame(height: 100)
.padding([.leading, .trailing], 100)
Button(action: {
}, label: {
Text("Forgot Password ?")
.underline()
.frame( height: 40, alignment: .trailing)
.padding(.leading, 320)
})
Button(action: {
}, label: {
Text("Sign In")
.frame(width: 200, height: 30)
.padding()
.background(Color.red)
.foregroundColor(Color.white)
.font(.custom("OpenSans-Bold", size: 28))
})
}
}
struct HelpView: View{
var body: some View{
Button(action: {
}, label: {
Text("Help")
.frame(maxWidth: .infinity, maxHeight: 80)
.background(.red)
.foregroundColor(.white)
.font(.custom("OpenSans-Bold", size: 28))
})
}
}

Email & Password validation (SwiftUI)

I have a problem generating an error text.
#State private var email = ""
#State private var password = "Vignesh123!"
private func isValidEmail(_ email: String) -> Bool {
let emailRegEx = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let emailPred = NSPredicate(format:"SELF MATCHES %#", emailRegEx)
return emailPred.evaluate(with: email)
}
private func isPasswordValid(_ password : String) -> Bool{
let passwordFormat = "^(?=.*[A-Z])(?=.*[a-z])(?=.*?[0-9])(?=.*[$#$#!%*?&])[A-Za-z\\d$#$#!%*?&]{8,}"
let passwordTest = NSPredicate(format: "SELF MATCHES %#", passwordFormat)
return passwordTest.evaluate(with: password)
}
private func validView() -> String? {
if !self.isValidEmail(email) {
return "Email is invalid"
}
if !self.isPasswordValid(password) {
return "Password is invalid"
}
// Do same like other validation as per needed
return nil
}
during the code I wrote, I used TextField for the password to appear.
VStack(spacing: 0) {
Spacer()
VStack(alignment: .leading) {
TextField("Email", text: $email)
.textFieldStyle(DefaultTextFieldStyle())
.padding(.leading)
.frame(height: 50)
.background(.gray.opacity(0.3))
.cornerRadius(10)
.autocapitalization(.none)
if password.isEmpty {
Text("Email is Not Valid")
.font(.footnote).foregroundColor(.red).hidden()
} else if (self.validView() != nil) {
Text("Email is Not Valid")
.font(.footnote).foregroundColor(.red)
.padding(3)
}
}
VStack(alignment: .leading) {
TextField("Password", text: $password)
.textFieldStyle(DefaultTextFieldStyle())
.padding(.leading)
.frame(height: 50)
.background(.gray.opacity(0.3))
.cornerRadius(10)
.autocapitalization(.none)
if password.isEmpty {
Text("Password is Not Valid")
.font(.footnote).foregroundColor(.red).hidden()
} else if (self.validView() != nil) {
Text("Password is Not Valid")
.font(.footnote).foregroundColor(.red)
.padding(3)
}
}
Button(action: {
// Show error message here
if let errorMessage = self.validView() {
print(errorMessage)
return
}
}, label: {
Text("Login")
.frame(height: 45).frame(maxWidth: .infinity)
.background(.blue)
.foregroundColor(.white)
.cornerRadius(10)
})
Spacer()
}.padding()
result should be an error text should appear when I enter an inappropriate email / password and click on the Login
the above codes are my attempts 👆
I may be making elementary mistakes, I hope you understand correctly.
I appreciate your attention, thank you all
why dont you use a #State var showErrorMessage:Bool
In your password and email valid functions toggle the showErrorMessage. So if password is wrong toggle to true and if password is not wrong toggle to false.
On button click call this showErrorMessage, if true then show the Text.

Show Selected ScrollView Item in Add New Item View SwiftUI

That is my projects link https://github.com/m3rtkoksal/TaskManager
This is my TaskListView
This is my NewTaskView
Unfortunately when I tap one of the items in scrollview again I see an empty NewTaskView. I should see NewTaskView with selectedTask datas instead.
That is my NewTaskView
struct NewTaskView: View {
#Environment(\.presentationMode) private var presentationMode
#StateObject private var obser = observer()
#State var taskTitle = ""
#State var taskFrom = ""
#State var taskFromDate = Date()
#State var taskToDate = Date()
#State var taskTo = ""
#State var taskNote = ""
#EnvironmentObject var task: SelectedTask
var body: some View {
NavigationView {
VStack(alignment: .leading) {
Group {
Text("Task Title")
TextField("Title", text:$taskTitle)
Divider()
Text("From")
DatePicker("", selection: $taskFromDate, in: Date()..., displayedComponents: [.date, .hourAndMinute])
.labelsHidden()
.onReceive(Just(taskFromDate)) { data in
taskFrom = getDate(date: taskFromDate)
}
Divider()
} .padding(.horizontal, 10)
.font(Font.custom("SFCompactDisplay-Bold", size: 25))
.foregroundColor(.gray)
Group {
Text("To")
DatePicker("", selection: $taskToDate, in: Date()..., displayedComponents: [.date, .hourAndMinute])
.labelsHidden()
.onReceive(Just(taskToDate)) { data in
taskTo = getDate(date: taskToDate)
}
Divider()
Text("Note")
TextField("Note", text:$taskNote)
}.padding(.horizontal, 10)
.font(Font.custom("SFCompactDisplay-Bold", size: 25))
.foregroundColor(.gray)
Button(action: {
let taskDictionary = [
"title" : self.taskTitle,
"dateTo": self.taskTo,
"dateFrom" : self.taskFrom,
"text": self.taskNote
]
let docRef = Firestore.firestore().document("tasks/\(UUID().uuidString)")
docRef.setData(taskDictionary) { (error) in
if let error = error {
print("error = \(error)")
} else {
print("success")
self.taskTitle = ""
self.taskNote = ""
}
}
}, label: {
ZStack {
RoundedRectangle(cornerRadius: 25)
.foregroundColor(Color(#colorLiteral(red: 0.4274509804, green: 0.2196078431, blue: 1, alpha: 1)))
Text("Create task")
.foregroundColor(.white)
.font(.title)
.fontWeight(.bold)
}
.padding(.horizontal, 10)
.frame(width: UIScreen.main.bounds.width - 20, height: 90)
})
}
}
.navigationBarTitle("Create a task")
.font(Font.custom("SFCompactDisplay-Bold", size: 30))
}
}
And this is how I append selectedTask and call NewTaskView()
import SwiftUI
struct TaskFrameView: View {
#ObservedObject private var obser = observer()
var body: some View {
VStack(alignment: .leading){
Text("Today task")
.padding()
ScrollViewTask()
}
}
}
struct ScrollViewTask: View {
#EnvironmentObject var selectedTask : SelectedTask
#State var shown: Bool = false
#ObservedObject private var obser = observer()
var body: some View {
ScrollView(.vertical) {
VStack {
ForEach(self.obser.tasks) { task in
TaskElementView(task:task)
.onTapGesture {
self.shown.toggle()
self.selectedTask.appendNewTask(task: task)
}
}
}
}
.onAppear {
self.obser.fetchData()
}
.fullScreenCover(isPresented: $shown, content: {
NewTaskView()
.environmentObject(selectedTask)
})
}
}
How should I modify NewTaskView to be able to see selectedTask datas when an item is selected on Scrollview?
I have tried like below but I am getting index out of range error when I try to add new item. Plus there must be a better way than doing it with if else
VStack(alignment: .leading) {
Group {
Text("Task Title")
if task.item[0].title == "" {
TextField("Title", text:$taskTitle)
} else {
TextField(task.item[0].title, text: $taskTitle)
}
You have to add these code in SceneDelegate.swift
var selectedTask = SelectedTask()
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: ContentView()
.environmentObject(selectedTask)
)
self.window = window
window.makeKeyAndVisible()
}
}

SwiftUI Passing arguments over to View is not always available

I have a problem with passing arguments over to a View in SwiftUI when calling it. I have this View
import SwiftUI
struct GoodsItemFilterView: View {
#Environment(\.presentationMode) var presentationMode
#State var ref1Array: [String] = []
#State var ref2Array: [String] = []
#State var ref3Array: [String] = []
#State var stockStatusArray: [String] = []
#State var zoneArray: [String] = []
#State var selectorRef1 = 0
#State var selectorRef2 = 0
#State var selectorRef3 = 0
#State var selectorStockStatus = 0
#State var selectorZone = 0
var body: some View {
NavigationView {
Form{
Section(header: Text("Zone"), content: {
Picker(selection: $selectorZone, label:
Text("Zone")) {
ForEach(0 ..< zoneArray.count, id:\.self) {
Text(self.zoneArray[$0])
}
}
})
Section(header: Text("References"), content: {
Picker(selection: $selectorRef1, label:
Text("Reference 1")) {
ForEach(0 ..< ref1Array.count, id:\.self) {
Text(self.ref1Array[$0])
}
}
Picker(selection: $selectorRef2, label:
Text("Reference 2")) {
ForEach(0 ..< ref2Array.count, id:\.self) {
Text(self.ref2Array[$0])
}
}
Picker(selection: $selectorRef3, label:
Text("Reference 3")) {
ForEach(0 ..< ref3Array.count, id:\.self) {
Text(self.ref3Array[$0])
}
}
})
Section(header: Text("Status"), content: {
Picker(selection: $selectorStockStatus, label:
Text("Condition")) {
ForEach(0 ..< stockStatusArray.count, id:\.self) {
Text(self.stockStatusArray[$0])
}
}
})
Button(action: {
self.selectorZone = 0
self.selectorRef1 = 0
self.selectorRef2 = 0
self.selectorRef3 = 0
self.selectorStockStatus = 0
}, label: {
HStack(){
Spacer()
Image(systemName: "return")
Text("Reset filters")
Spacer()
}
})
}.navigationBarTitle("Filter")
.navigationBarItems(leading: (
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}, label: {
Text("Cancel")
}
)
), trailing: (
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}, label: {
Text("Done")
}
)
))
}.onAppear{
self.ref1Array.insert("***ALL***", at: 0)
self.ref2Array.insert("***ALL***", at: 0)
self.ref3Array.insert("***ALL***", at: 0)
self.stockStatusArray.insert("***ALL***", at: 0)
self.zoneArray.insert("***ALL***", at: 0)
}
}
}
struct GoodsItemFilter_Previews: PreviewProvider {
static var previews: some View {
GoodsItemFilterView(ref1Array: ["MAX100", "MAX101", "MAX102"], ref2Array: ["REF2_100", "REF2_101"], ref3Array: ["REF3_100", "REF3_101"])
}
}
and when I call it I can pass over the values of the arrays as arguments:
GoodsItemFilterView(ref1Array: ["MAX100", "MAX101", "MAX102"], ref2Array: ["REF2_100", "REF2_101"], ref3Array: ["REF3_100", "REF3_101"])
Now I have another view which is basically a copy of this one with a few changed names etc
//
// OrderHeaderFilter.swift
// WMS Toolbox
//
// Created by Max on 2020-01-24.
// Copyright © 2020 Max. All rights reserved.
//
import SwiftUI
//import Combine
struct OrderHeaderFilterView: View {
#Environment(\.presentationMode) var presentationMode
#State var orderTypeArray: [String] = []
#State var carrierArray: [String] = []
#State var fromStatus2 = UserDefaults.standard.string(forKey: "view.orderHeaderFilter.fromStatus")
// #State private var fromStatus2 = "040"
#State private var direction = ""
#State private var fromStatus = ""
#State private var toStatus = ""
#State private var orderType = ""
#State var selectorOrderType = 0
#State var selectorCarrier = 0
#State private var selectorIndex = 1
#State private var fromStatusSelectorIndex = 6
#State private var toStatusSelectorIndex = 2
#State private var directions = ["Inbound","Outbound","Both"]
private var orderStatusFromArray: [String] = ["005", "010", "022", "025", "030", "035", "040", "045", "046", "047", "060"]
private var orderStatusToArray: [String] = ["005", "010", "022", "025", "030", "035", "040", "045", "046", "047", "060"]
#State var orderStatus = OrderStatus.s05
enum OrderStatus: String, CaseIterable, Identifiable {
case s05 = "005"
case s10 = "010"
case s22 = "022"
case s25 = "025"
case s30 = "030"
case s35 = "035"
case s40 = "040"
case s45 = "045"
case s46 = "046"
case s60 = "060"
var id: String { rawValue }
}
enum Direction: String, CaseIterable{
case outbound = "1"
case inbound = "2"
case both = "3"
init(type: String) {
switch type {
case "1": self = .outbound
case "2": self = .inbound
case "3": self = .both
default: self = .both
}
}
var text: String {
switch self {
case .outbound: return "Outbound"
case .inbound: return "Inbound"
case .both: return "Both"
}
}
}
init(){
//nothing here
}
var body: some View {
return NavigationView{
Form{
HStack{
Text("Direction")
Spacer()
Picker(selection: $direction, label:
Text("Direction")) {
ForEach(directions, id:\.self) {
status in
Text(status)
}
}
.pickerStyle(SegmentedPickerStyle())
}
Picker(selection: $fromStatus, label:
Text("From Status")) {
ForEach(orderStatusFromArray, id:\.self) {
status in
Text(status)
}
}
Picker(selection: $toStatus, label:
Text("To Status")) {
ForEach(orderStatusFromArray, id:\.self) {
status in
Text(status)
}
}
}.navigationBarTitle("Filter")
.navigationBarItems(leading: (
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}, label: {
Text("Cancel")
}
)
), trailing: (
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}, label: {
Text("Done")
}
)
))
}.onAppear{
self.direction = UserDefaults.standard.string(forKey: "view.orderHeaderFilter.direction")!
self.fromStatus = UserDefaults.standard.string(forKey: "view.orderHeaderFilter.fromStatus")!
self.toStatus = UserDefaults.standard.string(forKey: "view.orderHeaderFilter.toStatus")!
self.orderTypeArray.insert("***ALL***", at: 0)
self.carrierArray.insert("***ALL***", at: 0)
}
}
}
struct OrderHeaderFilter_Previews: PreviewProvider {
static var previews: some View {
OrderHeaderFilterView()
}
}
and when I call it, it is not prompting me to pass over the arrays as arguments:
OrderHeaderFilterView()
What is the difference between those 2 views that the one is asking for arguments on initilization and the other one isn't? To be clear, in the end I want to pass over the arguments, so GoodsItemFilterView() is doing exactly what I need.
EnvironmentObjects are not passed via init method they are implicitly injected. The orderTypeArray and carrierArray have already initial values. So OrderHeaderFilterView() does not prompt you for arguments.
Found the issue(s):
I had this piece in the code
init(){
//nothing here
}
This needs to be removed, otherwise it will not ask for any variables.
The other issue is the one I don't understand:
private var orderStatusFromArray: [String] = ["005", "010", "022", "025", "030", "035", "040", "045", "046", "047", "060"]
private var orderStatusToArray: [String] = ["005", "010", "022", "025", "030", "035", "040", "045", "046", "047", "060"]
If I change the var to let, it works as expected. Another option is to remove the private at the beginning. So it looks like as soon as you have a
private var ...
in your code, all arguments become private. Maybe I am missing something here but that seems like a bug to me.

View update problem with UITextField integration into SwiftUI

In the following code, I cannot seem to get the FORM view to update when I change the value (and therefore state) of some of the fields, when using a CustomTextField. I imagine that the problem is in the coordination between the CustomTextField and SwiftUI, but I get the values when I do the calculations (below), but I can't get the updated values to display in the relevant UITextFields onscreen.
Can anyone spot my error? Any ideas would be greatly appreciated.
Thanks a lot.
Chris
struct CustomTextField: UIViewRepresentable {
var tag:Int = 0
var placeholder:String?
var keyboardType:UIKeyboardType?
var textAlignment:NSTextAlignment?
#Binding var text: String
var onChange: (()->Void?)?
func makeCoordinator() -> Coordinator {
Coordinator(text: $text, onChange: onChange)
}
func makeUIView(context: UIViewRepresentableContext<CustomTextField>) -> UITextField {
let tmpView = UITextField()
tmpView.tag = tag
tmpView.delegate = context.coordinator as UITextFieldDelegate
tmpView.placeholder = placeholder
tmpView.textAlignment = textAlignment ?? .left
tmpView.keyboardType = keyboardType ?? .default
tmpView.addDoneButtonOnKeyboard()
return tmpView
}
func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<CustomTextField>) {
uiView.setContentHuggingPriority(.defaultHigh, for: .vertical)
uiView.setContentHuggingPriority(.defaultLow, for: .horizontal)
}
class Coordinator : NSObject, UITextFieldDelegate {
#Binding var text: String
var onChange:(()->Void?)?
init(text: Binding<String>, onChange: (()->Void?)?) {
self._text = text
self.onChange = onChange
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if let currentTag = textField.tag as Int? {
if currentTag == 1 {
if string.count > 0 /*&& !textField.text!.contains(".")*/ {
let tmpCents = textField.text?.replacingOccurrences(of: ".", with: "") ?? ""
let cents = Int( tmpCents + string) ?? 0
if cents == 0 {
textField.text = "0.00"
} else {
let dols = Float(cents)/Float(100)
textField.text = String(format: "%0.2f", dols)
}
self.text = textField.text!
return false
}
}
}
if let currentValue = textField.text as NSString? {
let proposedValue = currentValue.replacingCharacters(in: range, with: string)
text = proposedValue
}
return true
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
self.text = textField.text ?? ""
return true
}
func textFieldDidChange(_ textField: UITextField) {
self.text = textField.text ?? ""
}
func textFieldDidEndEditing(_ textField: UITextField) {
self.onChange?()
textField.resignFirstResponder()
}
}
}
struct DetailView: View {
#EnvironmentObject var log: GasLog
#Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
#State var amount = ""
#State var pickedDate = Date()
#State var kilometers = ""
#State var editingAmount = false
#State var litres = ""
#State var gasPrice = ""
#State var showAlert = false
#State var errorMessage = ""
#State var reloadContent = false
var dateClosedRange: ClosedRange<Date> {
let min = Calendar.current.date(byAdding: .day, value: -10, to: Date())!
let max = Calendar.current.date(byAdding: .day, value: 10, to: Date())!
return min...max
}
var body: some View {
VStack {
HStack {
Text("ADD GAS PURCHASE")
.font(defaultSectionFont)
}
Form {
DatePicker(selection: $pickedDate, displayedComponents: .date, label: {
Text("PURCHASE DATE").font(defaultFormFont)
} )
HStack {
Text("AMOUNT").font(defaultFormFont)
Spacer()
CustomTextField(tag: 1,
placeholder: "purchase amount",
keyboardType: .numberPad,
textAlignment: .right,
text: $amount,
onChange: nil)
}
HStack {
Text("LITRES").font(defaultFormFont)
Spacer()
CustomTextField(tag: 1, placeholder: "litres purchased", keyboardType: .numberPad,
textAlignment: .right, text: $litres, onChange: self.calcValues)
}
HStack {
Text("FUEL PRICE").font(defaultFormFont)
Spacer()
CustomTextField(tag: 1, placeholder: "fuel price", keyboardType: .numberPad,
textAlignment: .right, text: $gasPrice, onChange: self.calcValues)
}
HStack {
Text("KILOMETERS ON CAR").font(defaultFormFont)
Spacer()
CustomTextField(tag: 0, placeholder: "kilometers", keyboardType: .numberPad,
textAlignment: .right, text: $kilometers, onChange: nil)
}
}
HStack {
Spacer()
Button(action: {
self.cancelRecord()
}, label: {
Image(systemName: "return")
})
.padding()
.overlay(
RoundedRectangle(cornerRadius: CGFloat(8.0))
.stroke(Color.gray, lineWidth: CGFloat(2.0))
)
Spacer()
Button(action: {
self.commitRecord()
}, label: {
Image(systemName: "plus.square")
})
.padding()
.overlay(
RoundedRectangle(cornerRadius: CGFloat(8.0))
.stroke(Color.gray, lineWidth: CGFloat(2.0))
)
Spacer()
}
.padding()
.background(toolbarBackgroundColor)
}.alert(isPresented: $showAlert) {
Alert(title: Text("Error"), message: Text(self.errorMessage))
}
}
func calcValues() -> Void {
if !self.amount.isEmpty {
switch (!self.gasPrice.isEmpty, !self.litres.isEmpty) {
case (true, false) :
self.litres = String(format: "%0.2f", Float(self.amount)! / Float(self.gasPrice)!)
self.reloadContent = true
case (false, true) :
self.gasPrice = String(format: "%0.2f", Float(self.amount)! / Float(self.litres)!)
self.reloadContent = true
default :
self.reloadContent = false
}
}
}
func commitRecord() {
let log = GasLog.shared()
if self.amount.isEmpty || Float(self.amount) == 0.0 {
errorMessage = "Value of AMOUNT is invalid. Please re-enter."
showAlert = true
} else {
self.dismiss()
log.addLogItem(date: self.pickedDate,
amount: (self.amount.isEmpty ? 0.00 : Float(self.amount)!),
kilometers: (self.kilometers.isEmpty ? nil : Int(self.kilometers)),
gasPrice: (self.gasPrice.isEmpty ? nil : Float(self.gasPrice)),
litres: (self.litres.isEmpty ? nil : Float(self.litres)))
}
}
func cancelRecord() {
self.dismiss()
}
func dismiss() {
self.presentationMode.wrappedValue.dismiss()
}
}
Add uiView.text = text to updateUIView:
func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<CustomTextField>) {
uiView.setContentHuggingPriority(.defaultHigh, for: .vertical)
uiView.setContentHuggingPriority(.defaultLow, for: .horizontal)
uiView.text = text // add this
}

Resources