I'm trying to make real time email validation, but the simulator has strange behaviour. When the email IS NOT valid, it should be red, when it IS valid, the color of the text should be black, but some characters stay red.
struct EmailText: View {
#State var textFieldValue: String = ""
private func isValid(_ s: String) -> Bool {
// not real validation function, just for simplicity
return Int.random(in: 0 ... 1) == 1
}
var body: some View {
TextField("", text: $textFieldValue)
.foregroundColor(isValid(textFieldValue) ? .black : .red)
.padding()
}
}
Edit: I have updated the validation function from regex to simple condition, to exclude regex as a possible issue.
Solution: disable autocorrection.
TextField("", text: $textFieldValue)
.foregroundColor(isValid(textFieldValue) ? .black : .red)
.padding()
.autocorrectionDisabled() // <----
Most certainly a SwiftUI bug that causes interference between the text highlighting of spell check warnings and the foreground color.
Confirmed. Your RegEx is wrong. Use:
/^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/
To validate e-mail.
Here I have tested code, Maybe It will help you
struct Demo1: View {
#State private var email: String
#State var emailIsValid: Bool = true
public init(email: String = "")
{
self.email = email
}
var body: some View {
// Text(/*#START_MENU_TOKEN#*/"Hello, World!"/*#END_MENU_TOKEN#*/)
TextField("Email", text: $email)
.onChange(of: email) { newValue in
if(newValue.range(of:"^\\w+([-+.']\\w+)*#\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$", options: .regularExpression) != nil) {
self.emailIsValid = true
print("valid")
} else {
self.emailIsValid = false
print("invalid")
}
}
.foregroundColor(emailIsValid ? Color.green : Color.red)
}
}
Related
I have been working with xcode 12 and swiftui. In my app I have textFiel with a localizable placeholder in Spanish and English, I switch to xcode 13 and it doesn't show me my localizable placeholder
this only happens in TextField, with SecureField it does not happen even with Text
this is my code
struct ContentView: View {
#State var email:String = ""
var body: some View {
VStack () {
TextField("login5", text: self.$email)
.autocapitalization(.none)
.padding()
.background(RoundedRectangle(cornerRadius: 50).stroke(Color("grayColor")))
}.padding(.horizontal, 20)
}
}
Localizable.strings
"login5" = "Correo eléctronico";
with SecureField in ios15, you can use the prompt parameter to get your localized string:
SecureField("purpose", text: $password, prompt: Text("login6"))
or using the label:
SecureField(text: $password) {
Text("login6")
}
EDIT1:
This is the test code I'm using to show a working localized TextField and SecureField.
import SwiftUI
#main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
#State var email = ""
#State var password = ""
#State var isVisible = false
var body: some View {
VStack (spacing: 55) {
Button(action: { isVisible.toggle() }) {
Text("Toggle isVisible")
}
TextField("login5", text: $email).border(.black)
if isVisible {
TextField("login6", text: $password).border(.green)
} else {
SecureField("password", text: $password, prompt: Text("login6")).border(.red)
}
}.padding(.horizontal, 20)
}
}
Test Localizable.strings file.
"login5" = "hola login5";
"login6" = "contraseña";
EDIT2: alternative approach of manually using LocalizedStringKey,
TextField(LocalizedStringKey("login5"), text: $email)
Your main Problem is, like workingdog already said, you need to use text: $variable.
That means for you declare your variable as #State var password = "" and use it like this..
struct ContentView: View {
#State var password = ""
...
if self.visible{
TextField("login6", text: $password)
....
} else {
SecureField("login6", text: $password)
....
}
}
Btw. next time post your code as code not as picture. Its easier to help you :)
Hope I understand your problem correctly and this will be your solution.
I've had the exact same problem, going from Xcode 12 to 13. All of a sudden some (not all) of my text fields no longer show localized string. I was able to fix the problem by forcing:
TextField(LocalizedString("usernameLabel"), text: $username)
Instead of
Textfield("usernameLabel", text: $username)
I have a custom TextField where I detect when the user taps on it to change the look of the field, but I tried to add the same look for the TextEditor, but it doesn't have an onEditingChanged option.
This is my text field:
struct CustomTextField : View {
#Binding var text : String
#State private var isEditing = false
#State var isDisabled = false
init(text : Binding<String>, isDisabled : Bool = false ){
self._text = text
self.isDisabled = isDisabled
}
var body: some View {
TextField("", text: $text, onEditingChanged: {
isEditing = $0
})
.padding(8)
.clipShape(RoundedRectangle(cornerRadius: 4))
.background (
RoundedRectangle(cornerRadius: 4)
.stroke(isEditing ? Color("Accent") : Color("Gray"), lineWidth: isDisabled ? 0 : 1)
)
}
}
I thought of adding a tap gesture recognizer, but it will only detect when I click, not when I leave the text editor.
Is there a way to detect the focus state of a TextEditor?
Starting with the familiar bad news '#available', you can use the focused modifier, available from iOS 15.
struct ContentView: View {
#State private var text = "Hello focused!"
#FocusState private var isFocused: Bool
var body: some View {
TextEditor(text: $text)
.focused($isFocused)
.foregroundColor(isFocused ? .red : .black)
}
}
Note:
This does not work in the canvas preview, run on Simulator.
This is a simple use of FocusState. FocusState has many more options.
I started today to use SwiftUI an I want to code a small calculator. Therefore I want a textfield where the user can write a number. But the usual textfields only accepts strings
what can I do ?
If you want to force the TextField keyboard to be numeric, just do this:
TextField("Input", text: $input)
.keyboardType(.decimalPad)
where the keyboardType is a UIKeyboardType.
with SwiftUI 2.0 you could use something like this:
struct ContentView: View {
#State var theInt: Int?
#State var text = ""
var body: some View {
VStack {
TextField("enter a number", text: $text)
.onChange(of: text) {
let txt = $0.filter { "-0123456789".contains($0) }
if allowed(txt) {
theInt = Int(txt)
text = txt
} else {
text = String(txt.dropLast())
}
}
}
}
func allowed(_ str: String) -> Bool {
let num = str.starts(with: "-") ? String(str.dropFirst()) : str
return CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: num))
}
}
Continuing on my learning path of SwiftUI, I stumble on what seems like a simple problem, but not to me !
I am trying to display a Text in a colored border. The color depends on a condition that I set inside a func.
I declared a var Bool in order to keep track of that condition, but when I try to write to it, I get the dreaded Cannot assign to property: 'self' is immutable error.
If I declare that Bool using the #State wrapper, I then get the other dreaded Modifying state during view update, this will cause undefined behavior message.
If anyone can help, would be [again] much appreciated.
Here is a summary of my code, simplified for clarity :
import SwiftUI
import CoreData
struct ContentView: View {
var isCurrent = false
var body: some View {
VStack {
Spacer()
Text(minimumEventsCheck().0) // Currency string
.lineLimit(nil) // allows unlimited lines
.padding(.all)
.overlay(RoundedRectangle(cornerRadius: 10).stroke(isCurrent ? Color.green : Color.red, lineWidth: 1))
Text(minimumEventsCheck().1) // details string
.lineLimit(nil) // allows unlimited lines
.padding(.all)
.overlay(RoundedRectangle(cornerRadius: 10).stroke(isCurrent ? Color.green : Color.red, lineWidth: 1))
Spacer()
} // END of main VStack
} // END of body
private func minimumEventsCheck() -> (String, String) {
var aString = ""
var bString = ""
let refDate = Date ()
let date90Prior = refDate.addingTimeInterval(-7776000)
if date90Prior < refDate { // This is always true, but it's for demo only
aString = "True"
bString = "Also true"
isCurrent = true // TRIGGERS Xcode error : Cannot assign to property: 'self' is immutable
} else {
aString = "False"
bString = "Also false"
isCurrent = false // TRIGGERS Xcode error : Cannot assign to property: 'self' is immutable
}
return (aString, bString)
} // End of func currencyText() -> String
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
return ContentView().environment(\.managedObjectContext, context)
}
}
use
#State var isCurrent = false
The view is a struct and structs are by default immutable. Therefore SwiftUI has propertywrapper #State to change the struct/variables.
I see two possible purpose of trying to do that.
First is to check some date once one start. You can do that in init and store the result in a struct property like this:
struct ContentView: View {
var isCurrent = false
var aString: String
var bString: String
init(refDate: Date){
if refDate < Date().addingTimeInterval(-7776000) {
self.aString = "True"
self.bString = "Also true"
self.isCurrent = true
} else {
self.aString = "False"
self.bString = "Also false"
self.isCurrent = false
}
}
var body: some View {
VStack {
Spacer()
Text(self.aString) // Currency string
.lineLimit(nil) // allows unlimited lines
.padding(.all)
.overlay(RoundedRectangle(cornerRadius: 10).stroke(isCurrent ? Color.green : Color.red, lineWidth: 1))
Text(self.bString) // details string
.lineLimit(nil) // allows unlimited lines
.padding(.all)
.overlay(RoundedRectangle(cornerRadius: 10).stroke(isCurrent ? Color.green : Color.red, lineWidth: 1))
Spacer()
} // END of main VStack
}
}
In a parent View (or in sceneDelegate file if it is the root view) you will have to pass this date like this:
ContentView(refDate: Date())
Then, when you change the date outside of the View, this view will be triggered to initiate with new values, and the dates will be checked again. This is SwiftUI way of coding.
If you trying to make some kind of function that will updates this view from the outside, you will have to use a passthroughSubject when changing something, and catch notification in .onReceive modifier. This is the only way of updating values inside the View without it being initialize again.
OK, Got this to work the following way :
Got rid of the #State var Bool in my View struct.
Modified the func to return the tuple (String, String, Bool) iso ((String, String) and called minimumEventsCheck().2 to access it in code :
import SwiftUI
import CoreData
struct ContentView: View {
var body: some View {
VStack {
Spacer()
Text(minimumEventsCheck().0) // Currency string
.lineLimit(nil) // allows unlimited lines
.padding(.all)
.overlay(RoundedRectangle(cornerRadius: 10).stroke(minimumEventsCheck().2 ? Color.green : Color.red, lineWidth: 1))
Text(minimumEventsCheck().1) // details string
.lineLimit(nil) // allows unlimited lines
.padding(.all)
.overlay(RoundedRectangle(cornerRadius: 10).stroke(minimumEventsCheck().2 ? Color.green : Color.red, lineWidth: 1))
Spacer()
} // END of main VStack
} // END of body
private func minimumEventsCheck() -> (String, String, Bool) {
var isCurrent = false
var aString = ""
var bString = ""
let refDate = Date ()
let date90Prior = refDate.addingTimeInterval(-7776000)
if date90Prior < refDate { // This is always true, but it's for demo only
aString = "True"
bString = "Also true"
isCurrent = true
} else {
aString = "False"
bString = "Also false"
isCurrent = false
}
return (aString, bString, isCurrent)
} // End of func currencyText() -> String
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
return ContentView().environment(\.managedObjectContext, context)
}
}
I am trying to add a ClearButton to TextField in SwiftUI when the particular TextField is selected.
The closest I got was creating a ClearButton ViewModifier and adding it to the TextField using .modifer()
The only problem is ClearButton is permanent and does not disappear when TextField is deselected
TextField("Some Text" , text: $someBinding).modifier(ClearButton(text: $someBinding))
struct ClearButton: ViewModifier {
#Binding var text: String
public func body(content: Content) -> some View {
HStack {
content
Button(action: {
self.text = ""
}) {
Image(systemName: "multiply.circle.fill")
.foregroundColor(.secondary)
}
}
}
}
Use ZStack to position the clear button appear inside the TextField.
TextField("Some Text" , text: $someBinding).modifier(ClearButton(text: $someBinding))
struct ClearButton: ViewModifier
{
#Binding var text: String
public func body(content: Content) -> some View
{
ZStack(alignment: .trailing)
{
content
if !text.isEmpty
{
Button(action:
{
self.text = ""
})
{
Image(systemName: "delete.left")
.foregroundColor(Color(UIColor.opaqueSeparator))
}
.padding(.trailing, 8)
}
}
}
}
Use .appearance() to activate the button
var body: some View {
UITextField.appearance().clearButtonMode = .whileEditing
return TextField(...)
}
For reuse try with this:
func TextFieldUIKit(text: Binding<String>) -> some View{
UITextField.appearance().clearButtonMode = .whileEditing
return TextField("Nombre", text: text)
}
=== solution 1(best): Introspect https://github.com/siteline/SwiftUI-Introspect
import Introspect
TextField("", text: $text)
.introspectTextField(customize: {
$0.clearButtonMode = .whileEditing
})
=== solution 2: ViewModifier
public struct ClearButton: ViewModifier {
#Binding var text: String
public init(text: Binding<String>) {
self._text = text
}
public func body(content: Content) -> some View {
HStack {
content
Spacer()
Image(systemName: "multiply.circle.fill")
.foregroundColor(.secondary)
.opacity(text == "" ? 0 : 1)
.onTapGesture { self.text = "" } // onTapGesture or plainStyle button
}
}
}
Usage:
#State private var name: String
...
Form {
Section() {
TextField("NAME", text: $name).modifier(ClearButton(text: $name))
}
}
=== solution 3: global appearance
UITextField.appearance().clearButtonMode = .whileEditing
You can add another Binding in your modifier:
#Binding var visible: Bool
then bind it to opacity of the button:
.opacity(visible ? 1 : 0)
then add another State for checking textField:
#State var showClearButton = true
And lastly update the textfield:
TextField("Some Text", text: $someBinding, onEditingChanged: { editing in
self.showClearButton = editing
}, onCommit: {
self.showClearButton = false
})
.modifier( ClearButton(text: $someBinding, visible: $showClearButton))
Not exactly what you're looking for, but this will let you show/hide the button based on the text contents:
HStack {
if !text.isEmpty {
Button(action: {
self.text = ""
}) {
Image(systemName: "multiply.circle")
}
}
}
After initializing a new project we need to create a simple view modifier which we will apply later to our text field. The view modifier has the tasks to check for content in the text field element and display a clear button inside of it, if content is available. It also handles taps on the button and clears the content.
Let’s have a look at that view modifier:
import SwiftUI
struct TextFieldClearButton: ViewModifier {
#Binding var text: String
func body(content: Content) -> some View {
HStack {
content
if !text.isEmpty {
Button(
action: { self.text = "" },
label: {
Image(systemName: "delete.left")
.foregroundColor(Color(UIColor.opaqueSeparator))
}
)
}
}
}
}
The code itself should be self explanatory and easy to understand as there is no fancy logic included in our tasks.
We just wrap the textfield inside a HStack and add the button, if the text field is not empty. The button itself has a single action of deleting the value of the text field.
For the clear icon we use the delete.left icon from the SF Symbols 2 library by Apple, but you could also use another one or even your own custom one.
The binding of the modifier is the same as the one we apply to the text field. Without it we would not be able to check for content or clear the field itself.
Inside the ContentView.swift we now simply add a TextField element and apply our modifier to it — that’s all!
import SwiftUI
struct ContentView: View {
#State var exampleText: String = ""
var body: some View {
NavigationView {
Form {
Section {
TextField("Type in your Text here...", text: $exampleText)
.modifier(TextFieldClearButton(text: $exampleText))
.multilineTextAlignment(.leading)
}
}
.navigationTitle("Clear button example")
}
}
}
The navigation view and form inside of the ContentView are not required. You could also just add the TextField inside the body, but with a form it’s much clearer and beautiful. 🙈
And so our final result looks like this:
I found this answer from #NigelGee on "Hacking with Swift".
.onAppear {
UITextField.appearance().clearButtonMode = .whileEditing
}
It really helped me out.
Simplest solution I came up with
//
// ClearableTextField.swift
//
// Created by Fred on 21.11.22.
//
import SwiftUI
struct ClearableTextField: View {
var title: String
#Binding var text: String
init(_ title: String, text: Binding<String>) {
self.title = title
_text = text
}
var body: some View {
ZStack(alignment: .trailing) {
TextField(title, text: $text)
Image(systemName: "xmark.circle.fill")
.foregroundColor(.secondary)
.onTapGesture {
text = ""
}
}
}
}
struct ClearableTextField_Previews: PreviewProvider {
#State static var text = "some value"
static var previews: some View {
Form {
// replace TextField("Original", text: $text) with
ClearableTextField("Clear me", text: $text)
}
}
}