UITextField text change event
IosObjective CSwiftEvent HandlingUitextfielddelegateIos Problem Overview
How can I detect any text changes in a textField? The delegate method shouldChangeCharactersInRange
works for something, but it did not fulfill my need exactly. Since until it returns YES, the textField texts are not available to other observer methods.
e.g. in my code calculateAndUpdateTextFields
did not get the updated text, the user has typed.
Is their any way to get something like textChanged
Java event handler.
- (BOOL)textField:(UITextField *)textField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string
{
if (textField.tag == kTextFieldTagSubtotal
|| textField.tag == kTextFieldTagSubtotalDecimal
|| textField.tag == kTextFieldTagShipping
|| textField.tag == kTextFieldTagShippingDecimal)
{
[self calculateAndUpdateTextFields];
}
return YES;
}
Ios Solutions
Solution 1 - Ios
From proper way to do uitextfield text change call back:
> I catch the characters sent to a UITextField control something like this:
// Add a "textFieldDidChange" notification method to the text field control.
In Objective-C:
[textField addTarget:self
action:@selector(textFieldDidChange:)
forControlEvents:UIControlEventEditingChanged];
In Swift:
textField.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged)
> Then in the textFieldDidChange
method you can examine the contents of the textField, and reload your table view as needed.
You could use that and put calculateAndUpdateTextFields as your selector
.
Solution 2 - Ios
XenElement's answer is spot on.
The above can be done in interface builder too by right-clicking on the UITextField and dragging the "Editing Changed" send event to your subclass unit.
Solution 3 - Ios
to set the event listener:
[self.textField addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
to actually listen:
- (void)textFieldDidChange:(UITextField *)textField {
NSLog(@"text changed: %@", textField.text);
}
Solution 4 - Ios
Swift:
yourTextfield.addTarget(self, action: #selector(yourHandler(textField:)), for: .editingChanged)
Then, implement the callback function:
@objc final private func yourHandler(textField: UITextField) {
print("Text changed")
}
Solution 5 - Ios
As stated here: https://stackoverflow.com/questions/7010547/uitextfield-text-change-event#comment19243817_7010765, it seems that as of iOS 6 (iOS 6.0 and 6.1 checked) it is not possible to fully detect changes in UITextField
objects just by observing the UITextFieldTextDidChangeNotification
.
It seems that only those changes made directly by the built-in iOS keyboard are tracked now. This means that if you change your UITextField
object just by invoking something like this: myUITextField.text = @"any_text"
, you won't be notified about any changes at all.
I don't know if this is a bug or it is intended. Seems like a bug since I haven't found any reasonable explanation in documentation. This is also stated here: https://stackoverflow.com/questions/7010547/uitextfield-text-change-event#comment20149070_7010765.
My "solution" to this is to actually post a notification by myself for every change I make to my UITextField
(if that change is done without using the built-in iOS keyboard). Something like this:
myUITextField.text = @"I'm_updating_my_UITextField_directly_in_code";
NSNotification *myTextFieldUpdateNotification =
[NSNotification notificationWithName:UITextFieldTextDidChangeNotification
object:myUITextField];
[NSNotificationCenter.defaultCenter
postNotification:myTextFieldUpdateNotification];
This way you are 100% confident that you'll receive the same notification when you change the .text
property of your UITextField
object, either when you update it "manually" in your code or through the built-in iOS keyboard.
It is important to consider that, since this is not a documented behavior, this approach may lead to 2 notifications received for the same change in your UITextField
object. Depending on your needs (what you actually do when your UITextField.text
changes) this could be an inconvenience for you.
A slightly different approach would be to post a custom notification (this is, with a custom name other than UITextFieldTextDidChangeNotification
) if you actually need to know whether the notification was yours or "iOS-made".
EDIT:
I've just found a different approach which I think could be better:
This involves the Key-Value Observing (KVO) feature of Objective-C (http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/KeyValueObserving/KeyValueObserving.html#//apple_ref/doc/uid/10000177-BCICJDHA).
Basically, you register yourself as an observer of a property and if this property changes you get notified about it. The "principle" is quite similar to how NSNotificationCenter
works, being the main advantage that this approach works automatically also as of iOS 6 (without any special tweak like having to manually post notifications).
For our UITextField
-scenario this works just fine if you add this code to, for example, your UIViewController
that contains the text field:
static void *myContext = &myContext;
- (void)viewDidLoad {
[super viewDidLoad];
//Observing changes to myUITextField.text:
[myUITextField addObserver:self forKeyPath:@"text"
options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld
context:myContext];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context {
if(context == myContext) {
//Here you get notified every time myUITextField's "text" property is updated
NSLog(@"New value: %@ - Old value: %@",
[change objectForKey:NSKeyValueChangeNewKey],
[change objectForKey:NSKeyValueChangeOldKey]);
}
else
[super observeValueForKeyPath:keyPath ofObject:object
change:change context:context];
}
Credit to this answer regarding "context" management: https://stackoverflow.com/a/12097161/2078512
Note: Seems like while you are in the process of editing a UITextField
with the built-in iOS keyboard, the "text" property of the text field is not updated with every new letter typed/removed. Instead, the text field object gets updated "as a whole" after you resign the first responder status of the text field.
Solution 6 - Ios
We can easily configure that from Storyboard
, CTRL drag the @IBAction
and change event as following:
Solution 7 - Ios
Here in swift version for same.
textField.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)
func textFieldDidChange(textField: UITextField) {
}
Thanks
Solution 8 - Ios
I resolved the issue changing the behavior of shouldChangeChractersInRange. If you return NO the changes won't be applied by iOS internally, instead you have the opportunity to change it manually and perform any actions after the changes.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
//Replace the string manually in the textbox
textField.text = [textField.text stringByReplacingCharactersInRange:range withString:string];
//perform any logic here now that you are sure the textbox text has changed
[self didChangeTextInTextField:textField];
return NO; //this make iOS not to perform any action
}
Solution 9 - Ios
Swift Version tested:
//Somewhere in your UIViewController, like viewDidLoad(){ ... }
self.textField.addTarget(
self,
action: #selector(SearchViewController.textFieldDidChange(_:)),
forControlEvents: UIControlEvents.EditingChanged
)
Parameters explained:
self.textField //-> A UITextField defined somewhere in your UIViewController
self //-> UIViewController
.textFieldDidChange(_:) //-> Can be named anyway you like, as long as it is defined in your UIViewController
Then add the method you created above in your UIViewController
:
//Gets called everytime the text changes in the textfield.
func textFieldDidChange(textField: UITextField){
print("Text changed: " + textField.text!)
}
Solution 10 - Ios
Starting iOS 14 there is no need to make a selector, you can pass a UIAction
with a closure instead:
let editingChanged = UIAction { _ in
// Do something when text changes...
}
myTextField.addAction(editingChanged, for: .editingChanged)
Solution 11 - Ios
Swift 4
func addNotificationObservers() {
NotificationCenter.default.addObserver(self, selector: #selector(textFieldDidChangeAction(_:)), name: .UITextFieldTextDidChange, object: nil)
}
@objc func textFieldDidChangeAction(_ notification: NSNotification) {
let textField = notification.object as! UITextField
print(textField.text!)
}
Solution 12 - Ios
For Swift 3.0:
let textField = UITextField()
textField.addTarget(
nil,
action: #selector(MyClass.textChanged(_:)),
for: UIControlEvents.editingChanged
)
using class like:
class MyClass {
func textChanged(sender: Any!) {
}
}
Solution 13 - Ios
Swift 3 Version
yourTextField.addTarget(self, action: #selector(YourControllerName.textChanges(_:)), for: UIControlEvents.editingChanged)
And get the changes in here
func textChanges(_ textField: UITextField) {
let text = textField.text! // your desired text here
// Now do whatever you want.
}
Hope it helps.
Solution 14 - Ios
- KVO solutions do not work
KVO does NOT work in iOS for controls: http://stackoverflow.com/a/6352525/1402846 https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/KVO.html
- In the observing class, you do this:
Given that you know the text view you want to watch:
var watchedTextView: UITextView!
Do this:
NotificationCenter.default.addObserver(
self,
selector: #selector(changed),
name: UITextView.textDidChangeNotification,
object: watchedTextView)
#However, be careful with that:
-
it's likely you only want to call that once, so do not call it in, for example,
layoutSubviews
-
it's quite difficult to know when to best call it during your bring-up process. It will depend on your situation. Unfortunately there is no standard, locked-in solution
-
for example you usually certainly can not call it at
init
time, since of coursewatchedTextView
may not exist yet
.
- the next problem is that ...
#None of the notifications are called when text is changed programmatically.
This is a huge, age-old, and stupid, nuisance in iOS engineering.
Controls simply do not - end of story - call the notifcations when the .text property is changed programmatically.
This is insanely annoying because of course - obviously - every app ever made sets the text programmatically, such as clearing the field after the user posts, etc.
You have to subclass the text view (or similar control) like this:
class NonIdioticTextView: UIITextView {
override var text: String! {
// boilerplate code needed to make watchers work properly:
get {
return super.text
}
set {
super.text = newValue
NotificationCenter.default.post(
name: UITextView.textDidChangeNotification,
object: self)
}
}
}
(Tip - don't forget the super call has to come before ! the post call.)
There is no solution available, unless, you fix the control by subclassing as shown just above. That is the only solution.
Note that the notification
UITextView.textDidChangeNotification
results in
func textViewDidChangeSelection(_ textView: UITextView)
being called.
(Not textViewDidChange
.)
Solution 15 - Ios
Swift 3.1:
> Selector: ClassName.MethodName
cell.lblItem.addTarget(self, action: #selector(NewListScreen.fieldChanged(textfieldChange:)), for: .editingChanged)
func fieldChanged(textfieldChange: UITextField){
}
Solution 16 - Ios
You should use the notification to solve this problem,because the other method will listen to the input box not the actually input,especially when you use the Chinese input method. In viewDidload
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textFiledEditChanged:)
name:@"UITextFieldTextDidChangeNotification"
object:youTarget];
then
- (void)textFiledEditChanged:(NSNotification *)obj {
UITextField *textField = (UITextField *)obj.object;
NSString *toBestring = textField.text;
NSArray *currentar = [UITextInputMode activeInputModes];
UITextInputMode *currentMode = [currentar firstObject];
if ([currentMode.primaryLanguage isEqualToString:@"zh-Hans"]) {
UITextRange *selectedRange = [textField markedTextRange];
UITextPosition *position = [textField positionFromPosition:selectedRange.start offset:0];
if (!position) {
if (toBestring.length > kMaxLength)
textField.text = toBestring;
}
}
finally,you run,will done.
Solution 17 - Ios
Swift 3 Version:
class SomeClass: UIViewController, UITextFieldDelegate {
@IBOutlet weak var aTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
aTextField.delegate = self
aTextField.addTarget(self, action: #selector(SignUpVC.textFieldDidChange), for: UIControlEvents.editingChanged)
}
func textFieldDidChange(_ textField: UITextField) {
//TEXT FIELD CHANGED..SECRET STUFF
}
}
Don't forget to set the delegate.
Solution 18 - Ios
With closure:
class TextFieldWithClosure: UITextField {
var targetAction: (() -> Void)? {
didSet {
self.addTarget(self, action: #selector(self.targetSelector), for: .editingChanged)
}
}
func targetSelector() {
self.targetAction?()
}
}
and using
textField.targetAction? = {
// will fire on text changed
}
Solution 19 - Ios
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didChangeTextViewText:)
name:UITextFieldTextDidChangeNotification object:nil];
- (void) didChangeTextViewText {
//do something
}
Solution 20 - Ios
One thing is you may have multiple UITextFields. So, give them a tag and then you can switch on the tags. Here's how to setup an observer in any class pretty much.
private func setupTextFieldNotification() {
NotificationCenter.default.addObserver(forName: UITextField.textDidChangeNotification, object: nil, queue: OperationQueue.main) { (notification) in
if let textField = notification.object as? UITextField, textField.tag == 100, let text = textField.text {
print(#line, text)
}
}
}
deinit {
NotificationCenter.default.removeObserver(self)
}
Solution 21 - Ios
SwiftUI
If you are using the native SwiftUI TextField
or just using the UIKit UITextField
(here is how), you can observe for text changes like:
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 text: String = ""
var body: some View {
TextField("Enter text here", text: $text)
.onChange(of: text) {
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
with the help of the combine framework:
import Combine
.onReceive(Just(text)) {
print($0)
}
Note that you can use text.publisher
instead of Just(text)
but it returns the change instead of the entire value.
Solution 22 - Ios
Swift 4 Version
Using Key-Value Observing Notify objects about changes to the properties of other objects.
var textFieldObserver: NSKeyValueObservation?
textFieldObserver = yourTextField.observe(\.text, options: [.new, .old]) { [weak self] (object, changeValue) in
guard let strongSelf = self else { return }
print(changeValue)
}