Show placeholder and icon when TextField isEmpty - swift

I am trying to add an icon ( SFSymbol) called "exclamationmark.triangle" if the field is empty to identify that it's a required text field. Below is the method I used. It works, but not properly. When the user clicks on a text field and starts typing after the first character keyboard goes away, so the user has to retap to continue typing.
if name.isEmpty {
HStack {
TextField("First & Last Name", text: $name)
Image(systemName: "exclamationmark.triangle")
}
} else {
TextField("First & Last Name", text: $name)
}

struct ContentView : View {
#State var name: String = ""
var body: some View {
HStack {
TextField("First & Last Name", text: $name)
name.isEmpty ? Image(systemName: "exclamationmark.triangle") : nil
}
}
}

Related

Begin typing in a textfield after enabling editing with a button press?

I have a text field for storing the name of a user on their profile. I have an edit button to the right of the text field that enables editing so the user doesn't accidentally change their profile.
Here is my code:
struct ProfilePage: View {
#State private var name = ""
#State var nameEditEnabled = false
#AppStorage("NAME_KEY") var savedName = ""
var body: some View {
VStack {
HStack {
TextField("Name", text: $savedName)
.textFieldStyle(.roundedBorder)
.font(.title) .disableAutocorrection(true)
.onChange(of: name) {text in
self.savedName = name }
.onSubmit { nameEditEnabled = false }
.disabled(nameEditEnabled ? false : true)
Button(action: {self.nameEditEnabled.toggle()}) {
Image(systemName: "square.and.pencil")
}
}
}
.padding()
}
}
How would I go about making it so that when the button is pressed, the user automatically is put into the textfield and the keyboard pops up so that they can begin typing (without having to manually press the textfield)?
When I enable editing, it just enables the text field but doesn't have the user start typing in it (i.e. bring up the keyboard so that they can edit the text field).
EDIT: updated code for suggestions.
GIF of what my app is doing
You have to use focused for that see the below code:
#State private var name = ""
#State var nameEditEnabled = false
#FocusState var isFocusOn: Bool
VStack {
HStack {
TextField("Name", text: $name)
.focused($isFocusOn)
.textFieldStyle(.roundedBorder)
.font(.title) .disableAutocorrection(true)
.onSubmit { nameEditEnabled = false }
Button(action: {
isFocusOn = true
}) {
Image(systemName: "square.and.pencil")
}
}
}
.padding()

Different State Variables for New Textfields in SwiftUI

I am new to SwiftUI and there is a scenario in which I can add more than one person's data and every time I tap on the button, it will collect new person's data.
The scenario is like this:
I add data on one textfield, it updates on every textfield because there is only one state variable for the textfield. My problem is how can I add multiple State variables for the textfield as the textfields have no fixed number. My code is:
import SwiftUI
struct TextFieldText: View {
#State private var name = ""
#State private var email = ""
#State var totalValue: Int = 1
var body: some View {
VStack(spacing: 30) {
ForEach((1...totalValue).reversed(), id: \.self) {_ in
VStack {
CustomTextfield(text: $name, placeHolder: "Enter name", title: "Enter Name")
CustomTextfield(text: $email, placeHolder: "Enter email", title: "Enter Email")
}
}
Button {
print("add person tapped")
totalValue = totalValue + 1
} label: {
ZStack {
RoundedRectangle(cornerRadius: 60)
.frame(width: 180, height: 45)
.foregroundColor(Color(ColorName.appBlue.rawValue))
Text("Add another person")
.foregroundColor(.white)
.font(Font.custom(InterFont.bold.rawValue, size: 14))
}
}
Spacer()
}
}
}
struct TextFieldText_Previews: PreviewProvider {
static var previews: some View {
TextFieldText()
}
}
I want to add different data on every textfield. How can I achieve it in SwiftUI?
You want to handle multiple people but you have only one name and one email property.
You need an array. A swifty way is a custom struct
struct Person {
var name, email : String
}
In the view replace name and email with an empty array of Person
#State private var people = [Person]()
In the ForEach loop iterate over the indices and bind the text parameter to the person at given index.
I don't have your custom text fields, the code uses the default fields
ForEach(people.indices, id: \.self) { index in
VStack {
TextField("Enter name", text: $people[index].name)
TextField("Enter email", text: $people[index].email)
}
.padding()
}
Finally in the button action add a new Person to people
Button {
print("add person tapped")
people.append(Person(name: "", email: ""))

Remove padding on TextEditor

My custom text editor below once you click on the pen to edit, a new space appears so the text from before is not on the same line as the new one. How can I fix this? Here's a simple reproducible example:
struct SwiftUIView: View {
#State var name: String = "test"
#State var showEdit: Bool = true
var body: some View {
HStack {
HStack {
if(showEdit) {
CustomTextEditor.init(placeholder: "My unique name", text: $name)
.font(.headline)
} else {
Text(name)
.font(.headline)
}
}
Spacer()
Button(action: {
showEdit.toggle()
}) {
Image(systemName: "pencil")
.foregroundColor(.secondary)
}
}
}
}
struct CustomTextEditor: View {
let placeholder: String
#Binding var text: String
var body: some View {
ZStack {
if text.isEmpty {
Text(placeholder)
.foregroundColor(Color.primary.opacity(0.25))
}
TextEditor(text: $text)
}.onAppear() {
UITextView.appearance().backgroundColor = .clear
}.onDisappear() {
UITextView.appearance().backgroundColor = nil
}
}
}
I want it to have the same padding properies as inserting a simple Text("") so when I switch between Text("xyz") and TextEditor(text: $xyz) it has the same padding alignment. Right now TextEditor has a weird padding.
You will drive yourself insane trying to line up a Text and a TextEditor (or a TextField, for that matter), so don't try. Use another, disabled, TextEditor instead, and control the .opacity() on the top one depending upon whether the bound variable is empty or not. Like this:
struct CustomTextEditor: View {
#Binding var text: String
#State private var placeholder: String
init(placeholder: String, text: Binding<String>) {
_text = text
_placeholder = State(initialValue: placeholder)
}
var body: some View {
ZStack {
TextEditor(text: $placeholder)
.disabled(true)
TextEditor(text: $text)
.opacity(text == "" ? 0.7 : 1)
}
}
}
This view will show the placeholder if there is no text, and hide the placeholder as soon as there is text.
Edit:
You don't need the button, etc. in your other view. It becomes simply:
struct SwiftUIView: View {
#State var name: String = ""
var body: some View {
CustomTextEditor.init(placeholder: "My unique name", text: $name)
.font(.headline)
.padding()
}
}
and if you need a "Done" button on the keyboard, change your CustomTextEditor() to this:
struct CustomTextEditor: View {
#Binding var text: String
#State private var placeholder: String
#FocusState var isFocused: Bool
init(placeholder: String, text: Binding<String>) {
_text = text
_placeholder = State(initialValue: placeholder)
}
var body: some View {
ZStack {
TextEditor(text: $placeholder)
.disabled(true)
TextEditor(text: $text)
.opacity(text == "" ? 0.7 : 1)
.focused($isFocused)
}
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
Button {
isFocused = false
} label: {
Text("Done")
.foregroundColor(.accentColor)
.padding(.trailing)
}
}
}
}
}

How to pass a fieldtext value from view another view swiftui

I put the fieldtext in view called fieldtextmydesine and also put the button in view named login and I called fieldtextmydesin view and login view in contentview how do I print the field text value when I press the login button
So you want to use NavigationView and NavigationLink instead of a button.
struct ContentView: View {
#State var name: String = "Tim"
var body: some View {
NavigationView {
VStack {
TextField("Enter your name", text: $name)
Text("Hello, \(name)!")
NavigationLink(destination: SecondView(name: self.$name)){
Text("LogIn")
}
}
}
}
//Second ContenView
struct SecondView: View {
#Binding var name: String
var body: some View {
Text("Hello \(text)")
}
}
From what I understand from your question, you are trying to pass a value entered into a Text-Field from one View to another. If this is what your asking then this is the best solution.
This snippet can help you:
You can bind property to textfield like this
struct ContentView: View {
#State private var name: String = "Tim"
var body: some View {
VStack {
TextField("Enter your name", text: $name)
Text("Hello, \(name)!")
}
}
}
You can add button and print name on button tap line you need. You can pass name property to another text on tap. Or hide text view and show on tap and another ways
You can display print name either on the console or can display name in an alert. In the below snippet, to fetch name entered in text field on button click requires state variable instead of normal variable. It is created with #State keyword. State parameter manages the state in the View. So whenever there is change in state all the components that are associated with the state will be rendered again.
import SwiftUI
struct LoginUI: View {
#State var textName: String = ""
#State var showAlert = false
var body: some View {
VStack(alignment: .center, spacing: 2.0) {
TextField("Enter your name", text: $textName).padding(10)
Button(action: {
print("Entered name is \(self.textName)")
self.showAlert = true
}, label: {Text("Login")}).padding().background(Color.gray)
}
.padding(5.0)
.alert(isPresented: $showAlert) {
Alert(title: Text("Entered name is"), message: Text("\(self.textName)"))
}
}
}
struct LoginUI_Previews: PreviewProvider {
static var previews: some View {
LoginUI()
}
}

SwiftUI change TextBox placeholder based on HttpResponse

I am learning SwiftUI and I am trying to implement a Forgot Password Functionality . The text field will say by default Enter your email then an http call takes places if the email is found in our system then I would like the Text PlaceHolder to Say "Enter Verification Code" . I already have everything else working . This is my code below. They enter their email then the HTTP call handles the rest and returns either a 0 or 1 in a closure depending on if the email is found . In the code below if the foundEmail is 1 then the Text placeholder should change to Enter Verification Code
struct ForgotPassWordView: View {
#State private var textResponse = ""
var body: some View {
ZStack
{
Color.black
VStack {
NavigationView {
Form {
Section {
TextField("Enter Email", text: $textResponse)
// change to Verification Code if foundEmail is 1
}
Section {
HStack {
Spacer()
Button(action: {
if !self.textResponse.isEmpty {
_ = ForgotPasswordRequest(email: self.textResponse, section: 1) {(foundEmail) in
if foundEmail == 0 {
// not found do nothing
} else if foundEmail == 1
{
// found email change to : Enter Verification Code
}
}
}
}) {
Spacer()
Text("Submit").fontWeight(.bold).frame(width: 70.0)
Spacer()
}
Spacer()
}
}
}
.navigationBarTitle(Text(""))
}
.padding(.horizontal, 15.0)
Text("Error Response").foregroundColor(.white)
}
.frame(height: 400.0)
}.edgesIgnoringSafeArea(.all)
}
}
Hello there I think you can use two TextField and switch views using a boolean because the placeHolder of text field not Binding so it cannot be edit... let me show what I mean in code:
struct ContentView: View {
#State var email: String = ""
#State var verificationCode: String = ""
#State var showCodeField: Bool = false
var body: some View {
NavigationView {
VStack {
if (!showCodeField) {
TextField("Enter Valid Email", text: $email)
} else {
TextField("Enter Verification Code", text: $verificationCode)
}
Button(action: {
self.showCodeField.toggle()
}) {
Text("Verify Email")
}
}
.padding()
}
}
}
also if you have a complex view you can extract them as variables and control which one is visible or not like this:
var someField: some View {
ZStack(alignment: .center) {
RoundedRectangle(cornerRadius: 8).foregroundColor(Color.white)
TextField("Enter Email", text: $email)
}
}
}
and in body you just call it like this
var body: some View {
VStack {
if(isVisible) {
someField
}
}