How to Detect Live Changes on Textfield in Swiftui

How to detect live changes on TextField in SwiftUI?

You can create a binding with a custom closure, like this:

struct ContentView: View {
@State var location: String = ""

var body: some View {
let binding = Binding<String>(get: {
self.location
}, set: {
self.location = $0
// do whatever you want here
})

return VStack {
Text("Current location: \(location)")
TextField("Search Location", text: binding)
}

}
}

How to detect when a TextField becomes active in SwiftUI?

The answer is to initialize TextField with the onEditingChanged parameter.

We can then execute a closure conditionally depending upon whether the text field was edited or changes were committed:

TextField("", text: $email, onEditingChanged: { changed in
if changed {
// User began editing the text field
}
else {
// User tapped the return key
}
})

Observe changes on SwiftUI TextField

A simple custom binding is a good start for this task:

struct ContentView: View {
@State private var query = ""

var body: some View {

let binding = Binding<String>(
get: { self.query },
set: { self.query = $0; self.textFieldChanged($0) }
)

return NavigationView {
List {
TextField("Text", text: binding)
}
}
}

private func textFieldChanged(_ text: String) { print(text) }
}

It is tested and working

SwiftUI - detect change in Textfield

here is a simple example that maybe of use to you, using some of the comments advice. There are 3 main elements, the condition, the disabling and the color.

struct ContentView: View {
@State private var password = ""
@State private var passwordRepeat = ""

// here the condition you want
var isConfirmed: Bool {
if !password.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty &&
!passwordRepeat.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty &&
(password == passwordRepeat) {
return true
} else {
return false
}
}

var body: some View {
VStack {
SecureField("password", text: $password)
SecureField("password confirm", text: $passwordRepeat)
Button(action: { } ) {
Text("SAVE").accentColor(.white)
}
.disabled(!isConfirmed) // <-- here to enable/disable the button
.padding(20)
.background(isConfirmed ? Color.green : Color.gray) // <-- here to change color of the button
.cornerRadius(20)
}.frame(width: 333)
.textFieldStyle(RoundedBorderTextFieldStyle())
}
}

EDIT1:

here is an approach to make the condition work with when your Textfield already has content in it:

// your ProfileEditorRow equivalent
struct ContentView: View {
@ObservedObject var profileViewModel: ProfileViewModel
let refUserName: String // <-- reference user name

init(profileViewModel: ProfileViewModel) {
self.profileViewModel = profileViewModel
self.refUserName = profileViewModel.user.name // <-- here keep the initial user name
}

var isCondition: Bool {
return (refUserName == profileViewModel.name) // <-- here test for change
}

var body: some View {
VStack {
TextField("test",text: $profileViewModel.user.name)
Button(action: { print("----> SAVE") } ) {
Text("SAVE").accentColor(.white)
}
.disabled(isCondition) // <-- here to enable/disable the button
.padding(20)
.background(isCondition ? Color.gray : Color.green) // <-- here to change color of the button
.cornerRadius(20)
}.frame(width: 333)
.textFieldStyle(RoundedBorderTextFieldStyle())
}

EDIT2: sample with multiple elements in condition

You probably have a User struct with name, birthday etc... using this:

struct User {
var name
var birthday
var gender
// ....
}

struct ContentView: View {
@ObservedObject var profileViewModel: ProfileViewModel
let refUser: User // <-- reference user

init(profileViewModel: ProfileViewModel) {
self.profileViewModel = profileViewModel
self.refUser = profileViewModel.user // <-- here keep the initial user values
}

var isCondition: Bool {
return (refUser.name == profileViewModel.user.name) &&
(refUser.birthday == profileViewModel.user.birthday)
}

var body: some View {
VStack {
TextField("name",text: $profileViewModel.user.name)
TextField("birthday",text: $profileViewModel.user.birthday)
Button(action: { print("----> SAVE") } ) {
Text("SAVE").accentColor(.white)
}
.disabled(isCondition) // <-- here to enable/disable the button
.padding(20)
.background(isCondition ? Color.gray : Color.green) // <-- here to change color of the button
.cornerRadius(20)
}.frame(width: 333)
.textFieldStyle(RoundedBorderTextFieldStyle())
}
}

Swiftui Textfield not updating when value changes

The answer is simply that TextFields don't update while they are in focus. To solve this problem, you need to incorporate a @FocusState in to the view, and cause the TextField to lose focus right before updating the variable. You can test it in your own view by tapping your Text prior to tapping in to the TextField. You will see it updates just fine.

struct ButtonUpdateTextField: View {
@State private var amount: Double = 0.0
@FocusState var isFocused: Bool // <-- add here

var body: some View {
Form {
VStack {
HStack {
Text("Amount EUR")
Spacer()
TextField("Type amount", value: $amount, format: .number)
.keyboardType(.numberPad)
.multilineTextAlignment(.trailing)
.focused($isFocused) // <-- add here
}

Text("Set MAX (999)")
.frame(maxWidth: .infinity, alignment: .leading)
.onTapGesture {
print("before tap \(amount )")
isFocused = false // <-- add here
amount = 999
print("after tap \(amount)")
}

}
}
}
}

How to determine which Textfield is being changed in SwiftUI

Just use different functions as callbacks, like

TextField("", text: $phoneAuthViewModel.firstCodeField)
.onChange(of: phoneAuthViewModel.firstCodeField, perform: codeChanged(_:))

TextField("", text: $phoneAuthViewModel.country)
.onChange(of: phoneAuthViewModel.country, perform: countryChanged(_:))

func codeChanged(_ value: String) {
// handle code changed here
}

func countryChanged(_ value: String) {
// handle country changed here
}

Check Number of Characters Of Textfield in SwiftUI

You can use .onChange :

.onChange(of: name) { newValue in

if newValue.count >= 3 {
// Call function
}
}

Change value of Text while typing in TextField at a time without clicking anywhere - SwiftUI

SwiftUI is not like Cocoa. It is not event-driven, and there is no communication from one interface object to another. There is only data. Data flows thru state variables. It flows up by a state variable's binding and down by a state variable's value.

So you don't need an onEditingChanged event, or any other event. $totalAmount is a binding. It changes when the text field's text changes, automatically.

Moreover, totalAmount is a state variable. If the Text uses this as its value, directly or indirectly, the text changes automatically when totalAmount changes.

So just use a calculated variable that depends on totalAmount and make that the value of the Text and you're done.

Extremely basic example:

struct ContentView : View {
@State var totalAmount = ""
let addAmount = 10.0 // or whatever
var calc : Double { (Double(totalAmount) ?? 0.0) + addAmount } // or whatever
var body: some View {
HStack {
TextField("Type a number", text: $totalAmount)
.textFieldStyle(RoundedBorderTextFieldStyle())
.frame(width:100)
Text(String(self.calc))
}
}
}


Related Topics



Leave a reply



Submit