How to detect live changes on TextField in SwiftUI?

IosTextfieldSwiftui

Ios Problem Overview


I have a simple TextField that binds to the state 'location' like this,

TextField("Search Location", text: $location)

I want to call a function each time this field changes, something like this:

TextField("Search Location", text: $location) {
   self.autocomplete(location)
}

However this doesn't work. I know that there are callbacks, onEditingChanged - however this only seems to be triggered when the field is focussed.

How can I get this function to call each time the field is updated?

Ios Solutions


Solution 1 - Ios

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)
        }
        
    }
}

Solution 2 - Ios

SwiftUI 2.0

From iOS 14, macOS 11, or any other OS contains SwiftUI 2.0, there is a new modifier called .onChange that detects any change of the given state:

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

    var body: some View {
        TextField("Your Location", text: $location)
            .onChange(of: location) {
                print($0) // You can do anything due to the change here.
                // self.autocomplete($0) // like this
            }
    }
}

SwiftUI 1.0

For older iOS and other SwiftUI 1.0 platforms, you can use onReceive:

.onReceive(location.publisher) { 
    print($0)
}

Note that it returns the change instead of the entire value. If you need the behavior the same as the onChange, you can use the combine and follow the answer provided by @pawello2222.

Solution 3 - Ios

Another solution, if you need to work with a ViewModel, could be:

import SwiftUI
import Combine

class ViewModel: ObservableObject {
    @Published var location = "" {
        didSet {
            print("set")
            //do whatever you want
        }
    }
}

struct ContentView: View {
    @ObservedObject var viewModel = ViewModel()

    var body: some View {
        TextField("Search Location", text: $viewModel.location)
    }
}

Solution 4 - Ios

SwiftUI 1 & 2

Use onReceive:

import Combine
import SwiftUI

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

    var body: some View {
        TextField("Search Location", text: $location)
            .onReceive(Just(location)) { location in
                // print(location)
            }
    }
}

Solution 5 - Ios

While other answers work might work but this one worked for me where I needed to listen to the text change as well as react to it.

first step create one extension function.

extension Binding {
    func onChange(_ handler: @escaping (Value) -> Void) -> Binding<Value> {
        Binding(
            get: { self.wrappedValue },
            set: { newValue in
                self.wrappedValue = newValue
                handler(newValue)
            }
        )
    }
}

now call change on the binding in TextField something like below.

  TextField("hint", text: $text.onChange({ (value) in
      //do something here
  }))

source : HackingWithSwift

Solution 6 - Ios

What I found most useful was that TextField has a property that is called onEditingChanged which is called when editing starts and when editing completes.

TextField("Enter song title", text: self.$userData.songs[self.songIndex].name, onEditingChanged: { (changed) in
    if changed {
        print("text edit has begun")
    } else {
        print("committed the change")
        saveSongs(self.userData.songs)
    }
})
    .textFieldStyle(RoundedBorderTextFieldStyle())
    .font(.largeTitle)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiondylankbuckleyView Question on Stackoverflow
Solution 1 - IoskontikiView Answer on Stackoverflow
Solution 2 - IosMojtaba HosseiniView Answer on Stackoverflow
Solution 3 - IosmatteopucView Answer on Stackoverflow
Solution 4 - Iospawello2222View Answer on Stackoverflow
Solution 5 - Iosvikas kumarView Answer on Stackoverflow
Solution 6 - IosDave LevyView Answer on Stackoverflow